saha-ui 1.22.10 → 1.22.12

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 (168) hide show
  1. package/bin/cli.js +927 -200
  2. package/dist/components/Accordion/Accordion.d.ts +9 -0
  3. package/dist/components/Accordion/Accordion.d.ts.map +1 -0
  4. package/dist/components/Accordion/Accordion.js +324 -0
  5. package/dist/components/Accordion/Accordion.styles.d.ts +14 -0
  6. package/dist/components/Accordion/Accordion.styles.d.ts.map +1 -1
  7. package/dist/components/Accordion/Accordion.styles.js +83 -10
  8. package/dist/components/Accordion/Accordion.types.d.ts +108 -1
  9. package/dist/components/Accordion/Accordion.types.d.ts.map +1 -1
  10. package/dist/components/Accordion/index.d.ts +3 -6
  11. package/dist/components/Accordion/index.d.ts.map +1 -1
  12. package/dist/components/Accordion/index.js +7 -148
  13. package/dist/components/Affix/Affix.context.d.ts +15 -0
  14. package/dist/components/Affix/Affix.context.d.ts.map +1 -0
  15. package/dist/components/Affix/Affix.context.js +81 -0
  16. package/dist/components/Affix/Affix.d.ts +9 -0
  17. package/dist/components/Affix/Affix.d.ts.map +1 -0
  18. package/dist/components/Affix/Affix.hooks.d.ts +81 -0
  19. package/dist/components/Affix/Affix.hooks.d.ts.map +1 -0
  20. package/dist/components/Affix/Affix.hooks.js +278 -0
  21. package/dist/components/Affix/Affix.js +714 -0
  22. package/dist/components/Affix/Affix.styles.d.ts +14 -2
  23. package/dist/components/Affix/Affix.styles.d.ts.map +1 -1
  24. package/dist/components/Affix/Affix.styles.js +99 -28
  25. package/dist/components/Affix/Affix.types.d.ts +517 -45
  26. package/dist/components/Affix/Affix.types.d.ts.map +1 -1
  27. package/dist/components/Affix/Affix.utils.d.ts +89 -0
  28. package/dist/components/Affix/Affix.utils.d.ts.map +1 -0
  29. package/dist/components/Affix/Affix.utils.js +165 -0
  30. package/dist/components/Affix/index.d.ts +6 -47
  31. package/dist/components/Affix/index.d.ts.map +1 -1
  32. package/dist/components/Affix/index.js +47 -132
  33. package/dist/components/Alert/Alert.d.ts +5 -0
  34. package/dist/components/Alert/Alert.d.ts.map +1 -0
  35. package/dist/components/Alert/Alert.icons.d.ts +38 -0
  36. package/dist/components/Alert/Alert.icons.d.ts.map +1 -0
  37. package/dist/components/Alert/Alert.icons.js +165 -0
  38. package/dist/components/Alert/Alert.js +184 -0
  39. package/dist/components/Alert/Alert.styles.d.ts +24 -3
  40. package/dist/components/Alert/Alert.styles.d.ts.map +1 -1
  41. package/dist/components/Alert/Alert.styles.js +359 -54
  42. package/dist/components/Alert/Alert.types.d.ts +93 -28
  43. package/dist/components/Alert/Alert.types.d.ts.map +1 -1
  44. package/dist/components/Alert/AlertProvider.d.ts +6 -0
  45. package/dist/components/Alert/AlertProvider.d.ts.map +1 -0
  46. package/dist/components/Alert/AlertProvider.js +156 -0
  47. package/dist/components/Alert/index.d.ts +6 -4
  48. package/dist/components/Alert/index.d.ts.map +1 -1
  49. package/dist/components/Alert/index.js +20 -147
  50. package/dist/components/AppBar/AppBar.components.d.ts +88 -0
  51. package/dist/components/AppBar/AppBar.components.d.ts.map +1 -0
  52. package/dist/components/AppBar/AppBar.components.js +742 -0
  53. package/dist/components/AppBar/AppBar.context.d.ts +16 -0
  54. package/dist/components/AppBar/AppBar.context.d.ts.map +1 -0
  55. package/dist/components/AppBar/AppBar.context.js +37 -0
  56. package/dist/components/AppBar/AppBar.d.ts +10 -0
  57. package/dist/components/AppBar/AppBar.d.ts.map +1 -0
  58. package/dist/components/AppBar/AppBar.hooks.d.ts +49 -0
  59. package/dist/components/AppBar/AppBar.hooks.d.ts.map +1 -0
  60. package/dist/components/AppBar/AppBar.hooks.js +200 -0
  61. package/dist/components/AppBar/AppBar.js +486 -0
  62. package/dist/components/AppBar/AppBar.styles.d.ts +40 -3
  63. package/dist/components/AppBar/AppBar.styles.d.ts.map +1 -1
  64. package/dist/components/AppBar/AppBar.styles.js +476 -48
  65. package/dist/components/AppBar/AppBar.types.d.ts +529 -65
  66. package/dist/components/AppBar/AppBar.types.d.ts.map +1 -1
  67. package/dist/components/AppBar/index.d.ts +6 -56
  68. package/dist/components/AppBar/index.d.ts.map +1 -1
  69. package/dist/components/AppBar/index.js +42 -75
  70. package/dist/components/AspectRatio/AspectRatio.styles.d.ts +31 -6
  71. package/dist/components/AspectRatio/AspectRatio.styles.d.ts.map +1 -1
  72. package/dist/components/AspectRatio/AspectRatio.styles.js +157 -22
  73. package/dist/components/AspectRatio/AspectRatio.types.d.ts +220 -61
  74. package/dist/components/AspectRatio/AspectRatio.types.d.ts.map +1 -1
  75. package/dist/components/AspectRatio/LoadingIndicator.d.ts +36 -0
  76. package/dist/components/AspectRatio/LoadingIndicator.d.ts.map +1 -0
  77. package/dist/components/AspectRatio/LoadingIndicator.js +718 -0
  78. package/dist/components/AspectRatio/index.d.ts +3 -30
  79. package/dist/components/AspectRatio/index.d.ts.map +1 -1
  80. package/dist/components/AspectRatio/index.js +225 -65
  81. package/dist/components/Calendar/Calendar.styles.d.ts +1 -1
  82. package/dist/components/Carousel/Carousel.styles.d.ts +1 -1
  83. package/dist/components/Chart/charts/ComposedChartComponent.js +0 -1
  84. package/dist/components/Chart/components/ChartTooltip.js +0 -1
  85. package/dist/components/Checkbox/Checkbox.styles.d.ts +117 -4
  86. package/dist/components/Checkbox/Checkbox.styles.d.ts.map +1 -1
  87. package/dist/components/Checkbox/Checkbox.styles.js +482 -97
  88. package/dist/components/Checkbox/Checkbox.types.d.ts +174 -80
  89. package/dist/components/Checkbox/Checkbox.types.d.ts.map +1 -1
  90. package/dist/components/Checkbox/index.d.ts +9 -1
  91. package/dist/components/Checkbox/index.d.ts.map +1 -1
  92. package/dist/components/Checkbox/index.js +573 -228
  93. package/dist/components/DatePicker/DatePicker.styles.d.ts +2 -2
  94. package/dist/components/Dialog/Dialog.styles.d.ts +1 -1
  95. package/dist/components/Drawer/Drawer.styles.d.ts +1 -1
  96. package/dist/components/Form/Form.d.ts +83 -0
  97. package/dist/components/Form/Form.d.ts.map +1 -0
  98. package/dist/components/Form/Form.js +691 -0
  99. package/dist/components/Form/Form.styles.d.ts +34 -8
  100. package/dist/components/Form/Form.styles.d.ts.map +1 -1
  101. package/dist/components/Form/Form.styles.js +199 -113
  102. package/dist/components/Form/Form.types.d.ts +138 -164
  103. package/dist/components/Form/Form.types.d.ts.map +1 -1
  104. package/dist/components/Form/index.d.ts +2 -115
  105. package/dist/components/Form/index.d.ts.map +1 -1
  106. package/dist/components/Form/index.js +17 -336
  107. package/dist/components/HoverCard/HoverCard.styles.d.ts +1 -1
  108. package/dist/components/Input/index.d.ts +4 -39
  109. package/dist/components/Input/index.d.ts.map +1 -1
  110. package/dist/components/Input/index.js +123 -93
  111. package/dist/components/Item/index.js +1 -1
  112. package/dist/components/NavigationMenu/NavigationMenu.styles.d.ts +2 -2
  113. package/dist/components/Pagination/Pagination.styles.d.ts +1 -1
  114. package/dist/components/Rating/Rating.colors.d.ts +32 -0
  115. package/dist/components/Rating/Rating.colors.d.ts.map +1 -0
  116. package/dist/components/Rating/Rating.colors.js +149 -0
  117. package/dist/components/Rating/Rating.d.ts +10 -0
  118. package/dist/components/Rating/Rating.d.ts.map +1 -0
  119. package/dist/components/Rating/Rating.icons.d.ts +32 -0
  120. package/dist/components/Rating/Rating.icons.d.ts.map +1 -0
  121. package/dist/components/Rating/Rating.icons.js +209 -0
  122. package/dist/components/Rating/Rating.js +298 -0
  123. package/dist/components/Rating/Rating.styles.d.ts +18 -3
  124. package/dist/components/Rating/Rating.styles.d.ts.map +1 -1
  125. package/dist/components/Rating/Rating.styles.js +106 -65
  126. package/dist/components/Rating/Rating.types.d.ts +76 -13
  127. package/dist/components/Rating/Rating.types.d.ts.map +1 -1
  128. package/dist/components/Rating/index.d.ts +4 -16
  129. package/dist/components/Rating/index.d.ts.map +1 -1
  130. package/dist/components/Rating/index.js +11 -215
  131. package/dist/components/Resizable/Resizable.types.d.ts +7 -7
  132. package/dist/components/Resizable/Resizable.types.d.ts.map +1 -1
  133. package/dist/components/Resizable/index.d.ts +4 -4
  134. package/dist/components/Resizable/index.d.ts.map +1 -1
  135. package/dist/components/Resizable/index.js +53 -55
  136. package/dist/components/Skeleton/Skeleton.styles.d.ts +1 -1
  137. package/dist/components/Spinner/index.js +0 -1
  138. package/dist/components/Tab/Tab.styles.d.ts +2 -2
  139. package/dist/components/Table/Table.styles.d.ts +1 -1
  140. package/dist/components/Timeline/Timeline.styles.d.ts +2 -2
  141. package/dist/components/Toast/Toast.styles.d.ts +1 -1
  142. package/dist/hooks/index.d.ts +1 -1
  143. package/dist/hooks/index.d.ts.map +1 -1
  144. package/dist/hooks/useAccordion.d.ts +85 -20
  145. package/dist/hooks/useAccordion.d.ts.map +1 -1
  146. package/dist/hooks/useAccordion.js +141 -27
  147. package/dist/hooks/useAlert.d.ts +4 -0
  148. package/dist/hooks/useAlert.d.ts.map +1 -0
  149. package/dist/hooks/useAlert.js +14 -0
  150. package/dist/hooks/useAnimatedHeight.d.ts +51 -0
  151. package/dist/hooks/useAnimatedHeight.d.ts.map +1 -0
  152. package/dist/hooks/useAnimatedHeight.js +58 -0
  153. package/dist/hooks/useAspectRatio.d.ts +5 -81
  154. package/dist/hooks/useAspectRatio.d.ts.map +1 -1
  155. package/dist/hooks/useAspectRatio.js +177 -44
  156. package/dist/hooks/useLazyMount.d.ts +49 -0
  157. package/dist/hooks/useLazyMount.d.ts.map +1 -0
  158. package/dist/hooks/useLazyMount.js +37 -0
  159. package/dist/hooks/useMergedRef.d.ts +23 -0
  160. package/dist/hooks/useMergedRef.d.ts.map +1 -0
  161. package/dist/hooks/useMergedRef.js +22 -0
  162. package/dist/index.d.ts +18 -15
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js +988 -936
  165. package/dist/lib/Slot.d.ts +30 -1
  166. package/dist/lib/Slot.d.ts.map +1 -1
  167. package/dist/lib/Slot.js +20 -17
  168. package/package.json +21 -20
package/bin/cli.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { execSync } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
+ import readline from "node:readline";
7
8
 
8
9
  const R = process.cwd();
9
10
  const a = process.argv.slice(2);
@@ -18,6 +19,464 @@ const wr = (f, s) => {
18
19
  const fE = (f) => fs.existsSync(f) && fs.statSync(f).isFile();
19
20
  const dE = (d) => fs.existsSync(d) && fs.statSync(d).isDirectory();
20
21
 
22
+ // ============================================
23
+ // PROMPT UTILITY
24
+ // ============================================
25
+ const prompt = (question) => {
26
+ const rl = readline.createInterface({
27
+ input: process.stdin,
28
+ output: process.stdout,
29
+ });
30
+
31
+ return new Promise((resolve) => {
32
+ rl.question(question, (answer) => {
33
+ rl.close();
34
+ resolve(answer.trim().toLowerCase());
35
+ });
36
+ });
37
+ };
38
+
39
+ const confirmPrompt = async (question) => {
40
+ const answer = await prompt(`${question} (y/n): `);
41
+ return answer === "y" || answer === "yes";
42
+ };
43
+
44
+ const selectPrompt = async (question, options) => {
45
+ console.log(`\n${question}`);
46
+ options.forEach((opt, idx) => {
47
+ console.log(` ${idx + 1}) ${opt.label}`);
48
+ });
49
+ const answer = await prompt("Enter your choice (1/2/3): ");
50
+ const idx = parseInt(answer, 10) - 1;
51
+ return options[ idx ]?.value || options[ 0 ].value;
52
+ };
53
+
54
+ // ============================================
55
+ // CSS PARSING UTILITIES
56
+ // ============================================
57
+
58
+ /**
59
+ * Extract CSS block content (handles nested braces)
60
+ */
61
+ const extractBlock = (css, startPattern) => {
62
+ const match = css.match(startPattern);
63
+ if (!match) return null;
64
+
65
+ const startIdx = match.index + match[ 0 ].length;
66
+ let braceCount = 1;
67
+ let endIdx = startIdx;
68
+
69
+ while (braceCount > 0 && endIdx < css.length) {
70
+ if (css[ endIdx ] === "{") braceCount++;
71
+ if (css[ endIdx ] === "}") braceCount--;
72
+ endIdx++;
73
+ }
74
+
75
+ return {
76
+ fullMatch: css.substring(match.index, endIdx),
77
+ content: css.substring(startIdx, endIdx - 1).trim(),
78
+ startIndex: match.index,
79
+ endIndex: endIdx,
80
+ };
81
+ };
82
+
83
+ /**
84
+ * Parse CSS variables from a block
85
+ */
86
+ const parseCSSVariables = (content) => {
87
+ const variables = {};
88
+ const varRegex = /--([\w-]+)\s*:\s*([^;]+);/g;
89
+ let match;
90
+
91
+ while ((match = varRegex.exec(content)) !== null) {
92
+ variables[ `--${match[ 1 ]}` ] = match[ 2 ].trim();
93
+ }
94
+
95
+ return variables;
96
+ };
97
+
98
+ /**
99
+ * Parse @keyframes from CSS
100
+ */
101
+ const parseKeyframes = (css) => {
102
+ const keyframes = {};
103
+ const keyframeRegex = /@keyframes\s+([\w-]+)\s*\{/g;
104
+ let match;
105
+
106
+ while ((match = keyframeRegex.exec(css)) !== null) {
107
+ const name = match[ 1 ];
108
+ const block = extractBlock(css.substring(match.index), /@keyframes\s+[\w-]+\s*\{/);
109
+ if (block) {
110
+ keyframes[ name ] = block.fullMatch;
111
+ }
112
+ }
113
+
114
+ return keyframes;
115
+ };
116
+
117
+ /**
118
+ * Detect existing CSS structures
119
+ */
120
+ const detectExistingStructures = (css) => {
121
+ return {
122
+ hasRoot: /:root\s*\{/.test(css),
123
+ hasDark: /\.dark\s*\{/.test(css),
124
+ hasThemeInline: /@theme\s+inline\s*\{/.test(css),
125
+ hasLayerBase: /@layer\s+base\s*\{/.test(css),
126
+ hasLayerComponents: /@layer\s+components\s*\{/.test(css),
127
+ hasLayerUtilities: /@layer\s+utilities\s*\{/.test(css),
128
+ hasCustomVariant: /@custom-variant\s+dark/.test(css),
129
+ hasTailwindImport: /@import\s+["']tailwindcss["']/.test(css),
130
+ hasTailwindDirectives: /@tailwind\s+(base|components|utilities)/.test(css),
131
+ hasKeyframes: /@keyframes/.test(css),
132
+ hasGlassClass: /\.glass\s*\{/.test(css),
133
+ };
134
+ };
135
+
136
+ /**
137
+ * Extract :root variables from CSS
138
+ */
139
+ const extractRootVariables = (css) => {
140
+ const block = extractBlock(css, /:root\s*\{/);
141
+ return block ? parseCSSVariables(block.content) : {};
142
+ };
143
+
144
+ /**
145
+ * Extract .dark variables from CSS
146
+ */
147
+ const extractDarkVariables = (css) => {
148
+ const block = extractBlock(css, /\.dark\s*\{/);
149
+ return block ? parseCSSVariables(block.content) : {};
150
+ };
151
+
152
+ /**
153
+ * Merge CSS variables (add missing from source to target)
154
+ */
155
+ const mergeVariables = (existing, incoming) => {
156
+ const merged = { ...existing };
157
+ let addedCount = 0;
158
+
159
+ for (const [ key, value ] of Object.entries(incoming)) {
160
+ if (!(key in merged)) {
161
+ merged[ key ] = value;
162
+ addedCount++;
163
+ }
164
+ }
165
+
166
+ return { merged, addedCount };
167
+ };
168
+
169
+ /**
170
+ * Generate CSS variable block string
171
+ */
172
+ const generateVariableBlock = (variables, indent = " ") => {
173
+ return Object.entries(variables)
174
+ .map(([ key, value ]) => `${indent}${key}: ${value};`)
175
+ .join("\n");
176
+ };
177
+
178
+ /**
179
+ * Replace or update :root block in CSS
180
+ */
181
+ const updateRootBlock = (css, newVariables, mode = "merge") => {
182
+ const block = extractBlock(css, /:root\s*\{/);
183
+
184
+ if (!block) {
185
+ // No existing :root, add new one
186
+ const newBlock = `:root {\n${generateVariableBlock(newVariables)}\n}`;
187
+ return { css: `${newBlock}\n\n${css}`, added: Object.keys(newVariables).length };
188
+ }
189
+
190
+ if (mode === "replace") {
191
+ const newBlock = `:root {\n${generateVariableBlock(newVariables)}\n}`;
192
+ return {
193
+ css: css.substring(0, block.startIndex) + newBlock + css.substring(block.endIndex),
194
+ added: Object.keys(newVariables).length,
195
+ };
196
+ }
197
+
198
+ // Merge mode
199
+ const existingVars = parseCSSVariables(block.content);
200
+ const { merged, addedCount } = mergeVariables(existingVars, newVariables);
201
+ const newBlock = `:root {\n${generateVariableBlock(merged)}\n}`;
202
+
203
+ return {
204
+ css: css.substring(0, block.startIndex) + newBlock + css.substring(block.endIndex),
205
+ added: addedCount,
206
+ };
207
+ };
208
+
209
+ /**
210
+ * Replace or update .dark block in CSS
211
+ */
212
+ const updateDarkBlock = (css, newVariables, mode = "merge") => {
213
+ const block = extractBlock(css, /\.dark\s*\{/);
214
+
215
+ if (!block) {
216
+ // No existing .dark, add new one
217
+ const newBlock = `.dark {\n${generateVariableBlock(newVariables)}\n}`;
218
+ // Insert after :root if exists
219
+ const rootBlock = extractBlock(css, /:root\s*\{/);
220
+ if (rootBlock) {
221
+ return {
222
+ css: css.substring(0, rootBlock.endIndex) + "\n\n" + newBlock + css.substring(rootBlock.endIndex),
223
+ added: Object.keys(newVariables).length,
224
+ };
225
+ }
226
+ return { css: `${newBlock}\n\n${css}`, added: Object.keys(newVariables).length };
227
+ }
228
+
229
+ if (mode === "replace") {
230
+ const newBlock = `.dark {\n${generateVariableBlock(newVariables)}\n}`;
231
+ return {
232
+ css: css.substring(0, block.startIndex) + newBlock + css.substring(block.endIndex),
233
+ added: Object.keys(newVariables).length,
234
+ };
235
+ }
236
+
237
+ // Merge mode
238
+ const existingVars = parseCSSVariables(block.content);
239
+ const { merged, addedCount } = mergeVariables(existingVars, newVariables);
240
+ const newBlock = `.dark {\n${generateVariableBlock(merged)}\n}`;
241
+
242
+ return {
243
+ css: css.substring(0, block.startIndex) + newBlock + css.substring(block.endIndex),
244
+ added: addedCount,
245
+ };
246
+ };
247
+
248
+ /**
249
+ * Merge keyframes (add missing)
250
+ */
251
+ const mergeKeyframes = (css, incomingKeyframes) => {
252
+ const existingKeyframes = parseKeyframes(css);
253
+ let updatedCss = css;
254
+ let addedCount = 0;
255
+
256
+ const newKeyframes = [];
257
+ for (const [ name, keyframe ] of Object.entries(incomingKeyframes)) {
258
+ if (!(name in existingKeyframes)) {
259
+ newKeyframes.push(keyframe);
260
+ addedCount++;
261
+ }
262
+ }
263
+
264
+ if (newKeyframes.length > 0) {
265
+ // Add new keyframes at the end of @layer utilities or at the end of file
266
+ const utilitiesBlock = extractBlock(css, /@layer\s+utilities\s*\{/);
267
+ if (utilitiesBlock) {
268
+ const insertPoint = utilitiesBlock.endIndex - 1;
269
+ const keyframesStr = "\n\n" + newKeyframes.join("\n\n") + "\n";
270
+ updatedCss = css.substring(0, insertPoint) + keyframesStr + css.substring(insertPoint);
271
+ } else {
272
+ updatedCss = css + "\n\n" + newKeyframes.join("\n\n");
273
+ }
274
+ }
275
+
276
+ return { css: updatedCss, added: addedCount };
277
+ };
278
+
279
+ // ============================================
280
+ // SAHA-UI CSS VARIABLES (extracted from your CSS)
281
+ // ============================================
282
+ const SAHA_UI_ROOT_VARIABLES = {
283
+ "--radius": "0.625rem",
284
+ "--background": "oklch(0.98 0.003 200)",
285
+ "--foreground": "oklch(0.15 0.01 200)",
286
+ "--card": "oklch(1 0 0)",
287
+ "--card-foreground": "oklch(0.15 0.01 200)",
288
+ "--popover": "oklch(1 0 0)",
289
+ "--popover-foreground": "oklch(0.15 0.01 200)",
290
+ "--primary": "oklch(48.151% 0.23085 269.463)",
291
+ "--primary-foreground": "oklch(1 0 0)",
292
+ "--secondary": "oklch(0.65 0.25 340)",
293
+ "--secondary-foreground": "oklch(1 0 0)",
294
+ "--muted": "oklch(0.96 0.005 200)",
295
+ "--muted-foreground": "oklch(0.45 0.01 200)",
296
+ "--accent": "oklch(0.65 0.12 185)",
297
+ "--accent-foreground": "oklch(1 0 0)",
298
+ "--success": "oklch(0.60 0.15 145)",
299
+ "--success-foreground": "oklch(1 0 0)",
300
+ "--warning": "oklch(0.70 0.15 65)",
301
+ "--warning-foreground": "oklch(0.15 0.01 200)",
302
+ "--error": "oklch(0.60 0.20 25)",
303
+ "--error-foreground": "oklch(1 0 0)",
304
+ "--destructive": "oklch(0.60 0.20 25)",
305
+ "--destructive-foreground": "oklch(1 0 0)",
306
+ "--info": "oklch(0.60 0.15 250)",
307
+ "--info-foreground": "oklch(1 0 0)",
308
+ "--border": "oklch(0.92 0.005 200)",
309
+ "--input": "oklch(0.96 0.005 200)",
310
+ "--ring": "oklch(0.60 0.18 275)",
311
+ "--chart-1": "oklch(0.60 0.18 275)",
312
+ "--chart-2": "oklch(0.60 0.15 145)",
313
+ "--chart-3": "oklch(0.60 0.15 250)",
314
+ "--chart-4": "oklch(0.65 0.25 340)",
315
+ "--chart-5": "oklch(0.65 0.12 185)",
316
+ "--glass-bg": "oklch(1 0 0 / 0.25)",
317
+ "--glass-bg-hover": "oklch(1 0 0 / 0.35)",
318
+ "--glass-border": "oklch(0.60 0.18 275 / 0.15)",
319
+ "--glass-shadow": "0 8px 32px 0 oklch(0.60 0.18 275 / 0.12)",
320
+ "--glass-blur": "16px",
321
+ };
322
+
323
+ const SAHA_UI_DARK_VARIABLES = {
324
+ "--background": "oklch(0.08 0.005 200)",
325
+ "--foreground": "oklch(0.95 0.005 200)",
326
+ "--card": "oklch(0.12 0.01 200)",
327
+ "--card-foreground": "oklch(0.95 0.005 200)",
328
+ "--popover": "oklch(0.12 0.01 200)",
329
+ "--popover-foreground": "oklch(0.95 0.005 200)",
330
+ "--primary": "oklch(41.145% 0.14945 272.396)",
331
+ "--primary-foreground": "oklch(0.98 0.003 200)",
332
+ "--secondary": "oklch(0.70 0.25 340)",
333
+ "--secondary-foreground": "oklch(0.98 0.003 200)",
334
+ "--muted": "oklch(0.15 0.01 200)",
335
+ "--muted-foreground": "oklch(0.65 0.005 200)",
336
+ "--accent": "oklch(0.70 0.15 185)",
337
+ "--accent-foreground": "oklch(0.98 0.003 200)",
338
+ "--success": "oklch(0.65 0.18 145)",
339
+ "--success-foreground": "oklch(0.98 0.003 200)",
340
+ "--warning": "oklch(0.75 0.18 65)",
341
+ "--warning-foreground": "oklch(0.98 0.003 200)",
342
+ "--error": "oklch(0.65 0.22 25)",
343
+ "--error-foreground": "oklch(0.98 0.003 200)",
344
+ "--destructive": "oklch(0.65 0.22 25)",
345
+ "--destructive-foreground": "oklch(0.98 0.003 200)",
346
+ "--info": "oklch(0.65 0.18 250)",
347
+ "--info-foreground": "oklch(0.98 0.003 200)",
348
+ "--border": "oklch(0.20 0.01 200)",
349
+ "--input": "oklch(0.15 0.01 200)",
350
+ "--ring": "oklch(0.68 0.20 275)",
351
+ "--chart-1": "oklch(0.68 0.20 275)",
352
+ "--chart-2": "oklch(0.65 0.18 145)",
353
+ "--chart-3": "oklch(0.65 0.18 250)",
354
+ "--chart-4": "oklch(0.70 0.25 340)",
355
+ "--chart-5": "oklch(0.70 0.15 185)",
356
+ "--glass-bg": "oklch(0.12 0.01 200 / 0.5)",
357
+ "--glass-bg-hover": "oklch(0.12 0.01 200 / 0.7)",
358
+ "--glass-border": "oklch(0.68 0.20 275 / 0.2)",
359
+ "--glass-shadow": "0 8px 32px 0 oklch(0 0 0 / 0.6)",
360
+ "--glass-blur": "16px",
361
+ };
362
+
363
+ const SAHA_UI_KEYFRAMES = {
364
+ "gradient-x": `@keyframes gradient-x {
365
+ 0%, 100% {
366
+ background-position: 0% 50%;
367
+ }
368
+ 50% {
369
+ background-position: 100% 50%;
370
+ }
371
+ }`,
372
+ "draw-circle": `@keyframes draw-circle {
373
+ to {
374
+ stroke-dashoffset: 0;
375
+ }
376
+ }`,
377
+ "draw-check": `@keyframes draw-check {
378
+ to {
379
+ stroke-dashoffset: 0;
380
+ }
381
+ }`,
382
+ "draw-x": `@keyframes draw-x {
383
+ to {
384
+ stroke-dashoffset: 0;
385
+ }
386
+ }`,
387
+ "shake": `@keyframes shake {
388
+ 0%, 100% {
389
+ transform: translateX(0);
390
+ }
391
+ 25% {
392
+ transform: translateX(-2px);
393
+ }
394
+ 75% {
395
+ transform: translateX(2px);
396
+ }
397
+ }`,
398
+ "bounce-in": `@keyframes bounce-in {
399
+ 0% {
400
+ transform: scale(0);
401
+ opacity: 0;
402
+ }
403
+ 50% {
404
+ transform: scale(1.2);
405
+ }
406
+ 100% {
407
+ transform: scale(1);
408
+ opacity: 1;
409
+ }
410
+ }`,
411
+ "jelly": `@keyframes jelly {
412
+ 0% { transform: scale(1, 1); }
413
+ 30% { transform: scale(1.25, 0.75); }
414
+ 40% { transform: scale(0.75, 1.25); }
415
+ 50% { transform: scale(1.15, 0.85); }
416
+ 65% { transform: scale(0.95, 1.05); }
417
+ 75% { transform: scale(1.05, 0.95); }
418
+ 100% { transform: scale(1, 1); }
419
+ }`,
420
+ "rubber-band": `@keyframes rubber-band {
421
+ 0% { transform: scale(1); }
422
+ 30% { transform: scale(1.25, 0.75); }
423
+ 40% { transform: scale(0.75, 1.25); }
424
+ 50% { transform: scale(1.15, 0.85); }
425
+ 65% { transform: scale(0.95, 1.05); }
426
+ 75% { transform: scale(1.05, 0.95); }
427
+ 100% { transform: scale(1); }
428
+ }`,
429
+ "swing": `@keyframes swing {
430
+ 20% { transform: rotate(15deg); }
431
+ 40% { transform: rotate(-10deg); }
432
+ 60% { transform: rotate(5deg); }
433
+ 80% { transform: rotate(-5deg); }
434
+ 100% { transform: rotate(0deg); }
435
+ }`,
436
+ "tada": `@keyframes tada {
437
+ 0% { transform: scale(1); }
438
+ 10%, 20% { transform: scale(0.9) rotate(-3deg); }
439
+ 30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
440
+ 40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
441
+ 100% { transform: scale(1) rotate(0); }
442
+ }`,
443
+ "heartbeat": `@keyframes heartbeat {
444
+ 0% { transform: scale(1); }
445
+ 14% { transform: scale(1.3); }
446
+ 28% { transform: scale(1); }
447
+ 42% { transform: scale(1.3); }
448
+ 70% { transform: scale(1); }
449
+ }`,
450
+ "progress-stripes": `@keyframes progress-stripes {
451
+ 0% { background-position: 0 0; }
452
+ 100% { background-position: 1.5rem 0; }
453
+ }`,
454
+ "progress-indeterminate": `@keyframes progress-indeterminate {
455
+ 0% { left: -40%; transform: scaleX(0.6); }
456
+ 50% { transform: scaleX(1); }
457
+ 100% { left: 100%; transform: scaleX(0.6); }
458
+ }`,
459
+ "progress-shimmer": `@keyframes progress-shimmer {
460
+ 0% { transform: translateX(-100%) scaleX(0); opacity: 0; }
461
+ 10% { opacity: 1; }
462
+ 50% { transform: translateX(0%) scaleX(1); }
463
+ 90% { opacity: 1; }
464
+ 100% { transform: translateX(100%) scaleX(0); opacity: 0; }
465
+ }`,
466
+ "progress-glow-pulse": `@keyframes progress-glow-pulse {
467
+ 0%, 100% { filter: brightness(1) saturate(1); }
468
+ 50% { filter: brightness(1.15) saturate(1.2); }
469
+ }`,
470
+ "collapsible-down": `@keyframes collapsible-down {
471
+ from { height: 0; opacity: 0; }
472
+ to { height: var(--radix-collapsible-content-height); opacity: 1; }
473
+ }`,
474
+ "collapsible-up": `@keyframes collapsible-up {
475
+ from { height: var(--radix-collapsible-content-height); opacity: 1; }
476
+ to { height: 0; opacity: 0; }
477
+ }`,
478
+ };
479
+
21
480
  // package.json
22
481
  const P = (() => {
23
482
  try {
@@ -206,7 +665,7 @@ const pick = () => {
206
665
  const M = "/* saha-ui */";
207
666
  const TW = /@import\s+["']tailwindcss["'];?/;
208
667
 
209
- // CSS for Tailwind v4+
668
+ // Full CSS for Tailwind v4 (when doing full replace or fresh install)
210
669
  const CSS_V4 = `@import "tailwindcss";
211
670
 
212
671
  @custom-variant dark (&:is(.dark *));
@@ -441,112 +900,228 @@ const CSS_V4 = `@import "tailwindcss";
441
900
  }
442
901
 
443
902
  @layer utilities {
903
+ @keyframes gradient-x {
904
+ 0%, 100% { background-position: 0% 50%; }
905
+ 50% { background-position: 100% 50%; }
906
+ }
907
+
908
+ @keyframes draw-circle {
909
+ to { stroke-dashoffset: 0; }
910
+ }
911
+
912
+ @keyframes draw-check {
913
+ to { stroke-dashoffset: 0; }
914
+ }
915
+
916
+ @keyframes draw-x {
917
+ to { stroke-dashoffset: 0; }
918
+ }
919
+
920
+ @keyframes shake {
921
+ 0%, 100% { transform: translateX(0); }
922
+ 25% { transform: translateX(-2px); }
923
+ 75% { transform: translateX(2px); }
924
+ }
925
+
926
+ @keyframes bounce-in {
927
+ 0% { transform: scale(0); opacity: 0; }
928
+ 50% { transform: scale(1.2); }
929
+ 100% { transform: scale(1); opacity: 1; }
930
+ }
931
+
932
+ @keyframes jelly {
933
+ 0% { transform: scale(1, 1); }
934
+ 30% { transform: scale(1.25, 0.75); }
935
+ 40% { transform: scale(0.75, 1.25); }
936
+ 50% { transform: scale(1.15, 0.85); }
937
+ 65% { transform: scale(0.95, 1.05); }
938
+ 75% { transform: scale(1.05, 0.95); }
939
+ 100% { transform: scale(1, 1); }
940
+ }
941
+
942
+ @keyframes rubber-band {
943
+ 0% { transform: scale(1); }
944
+ 30% { transform: scale(1.25, 0.75); }
945
+ 40% { transform: scale(0.75, 1.25); }
946
+ 50% { transform: scale(1.15, 0.85); }
947
+ 65% { transform: scale(0.95, 1.05); }
948
+ 75% { transform: scale(1.05, 0.95); }
949
+ 100% { transform: scale(1); }
950
+ }
951
+
952
+ @keyframes swing {
953
+ 20% { transform: rotate(15deg); }
954
+ 40% { transform: rotate(-10deg); }
955
+ 60% { transform: rotate(5deg); }
956
+ 80% { transform: rotate(-5deg); }
957
+ 100% { transform: rotate(0deg); }
958
+ }
959
+
960
+ @keyframes tada {
961
+ 0% { transform: scale(1); }
962
+ 10%, 20% { transform: scale(0.9) rotate(-3deg); }
963
+ 30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
964
+ 40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
965
+ 100% { transform: scale(1) rotate(0); }
966
+ }
967
+
968
+ @keyframes heartbeat {
969
+ 0% { transform: scale(1); }
970
+ 14% { transform: scale(1.3); }
971
+ 28% { transform: scale(1); }
972
+ 42% { transform: scale(1.3); }
973
+ 70% { transform: scale(1); }
974
+ }
975
+
444
976
  @keyframes progress-stripes {
445
- 0% {
446
- background-position: 0 0;
447
- }
448
- 100% {
449
- background-position: 1.5rem 0;
450
- }
977
+ 0% { background-position: 0 0; }
978
+ 100% { background-position: 1.5rem 0; }
451
979
  }
452
-
980
+
453
981
  @keyframes progress-indeterminate {
454
- 0% {
455
- left: -40%;
456
- transform: scaleX(0.6);
457
- }
458
- 50% {
459
- transform: scaleX(1);
460
- }
461
- 100% {
462
- left: 100%;
463
- transform: scaleX(0.6);
464
- }
982
+ 0% { left: -40%; transform: scaleX(0.6); }
983
+ 50% { transform: scaleX(1); }
984
+ 100% { left: 100%; transform: scaleX(0.6); }
465
985
  }
466
-
986
+
467
987
  @keyframes progress-shimmer {
468
- 0% {
469
- transform: translateX(-100%) scaleX(0);
470
- opacity: 0;
471
- }
472
- 10% {
473
- opacity: 1;
474
- }
475
- 50% {
476
- transform: translateX(0%) scaleX(1);
477
- }
478
- 90% {
479
- opacity: 1;
480
- }
481
- 100% {
482
- transform: translateX(100%) scaleX(0);
483
- opacity: 0;
484
- }
988
+ 0% { transform: translateX(-100%) scaleX(0); opacity: 0; }
989
+ 10% { opacity: 1; }
990
+ 50% { transform: translateX(0%) scaleX(1); }
991
+ 90% { opacity: 1; }
992
+ 100% { transform: translateX(100%) scaleX(0); opacity: 0; }
485
993
  }
486
-
994
+
487
995
  @keyframes progress-glow-pulse {
488
- 0%, 100% {
489
- filter: brightness(1) saturate(1);
490
- }
491
- 50% {
492
- filter: brightness(1.15) saturate(1.2);
493
- }
996
+ 0%, 100% { filter: brightness(1) saturate(1); }
997
+ 50% { filter: brightness(1.15) saturate(1.2); }
494
998
  }
495
-
999
+
496
1000
  @keyframes collapsible-down {
497
- from {
498
- height: 0;
499
- opacity: 0;
500
- }
501
- to {
502
- height: var(--radix-collapsible-content-height);
503
- opacity: 1;
504
- }
1001
+ from { height: 0; opacity: 0; }
1002
+ to { height: var(--radix-collapsible-content-height); opacity: 1; }
505
1003
  }
506
-
1004
+
507
1005
  @keyframes collapsible-up {
508
- from {
509
- height: var(--radix-collapsible-content-height);
510
- opacity: 1;
511
- }
512
- to {
513
- height: 0;
514
- opacity: 0;
515
- }
1006
+ from { height: var(--radix-collapsible-content-height); opacity: 1; }
1007
+ to { height: 0; opacity: 0; }
516
1008
  }
517
-
1009
+ /* Progress bar animation */
1010
+ @keyframes progress-indeterminate {
1011
+ 0% {
1012
+ transform: translateX(-100%);
1013
+ }
1014
+ 100% {
1015
+ transform: translateX(400%);
1016
+ }
1017
+ }
1018
+
1019
+ .animate-progress-indeterminate {
1020
+ animation: progress-indeterminate 1.5s ease-in-out infinite;
1021
+ }
1022
+
1023
+ /* Tailwind animate-in classes (if not using tailwindcss-animate) */
1024
+ @keyframes enter {
1025
+ from {
1026
+ opacity: var(--tw-enter-opacity, 1);
1027
+ transform: translate3d(
1028
+ var(--tw-enter-translate-x, 0),
1029
+ var(--tw-enter-translate-y, 0),
1030
+ 0
1031
+ )
1032
+ scale3d(
1033
+ var(--tw-enter-scale, 1),
1034
+ var(--tw-enter-scale, 1),
1035
+ var(--tw-enter-scale, 1)
1036
+ )
1037
+ rotate(var(--tw-enter-rotate, 0));
1038
+ }
1039
+ }
1040
+
1041
+ .animate-in {
1042
+ animation-name: enter;
1043
+ animation-duration: 150ms;
1044
+ --tw-enter-opacity: initial;
1045
+ --tw-enter-scale: initial;
1046
+ --tw-enter-rotate: initial;
1047
+ --tw-enter-translate-x: initial;
1048
+ --tw-enter-translate-y: initial;
1049
+ }
1050
+
1051
+ .fade-in {
1052
+ --tw-enter-opacity: 0;
1053
+ }
1054
+
1055
+ .slide-in-from-top {
1056
+ --tw-enter-translate-y: -100%;
1057
+ }
1058
+
1059
+ .slide-in-from-bottom {
1060
+ --tw-enter-translate-y: 100%;
1061
+ }
1062
+
1063
+ .duration-300 {
1064
+ animation-duration: 300ms;
1065
+ }
1066
+ /* Utility classes */
1067
+ .animate-bounce-in { animation: bounce-in 0.3s ease-out; }
1068
+ .animate-shake { animation: shake 0.5s ease-in-out; }
1069
+ .animate-jelly { animation: jelly 0.5s ease-in-out; }
1070
+ .animate-rubber-band { animation: rubber-band 0.5s ease-in-out; }
1071
+ .animate-swing { animation: swing 0.5s ease-in-out; }
1072
+ .animate-tada { animation: tada 0.5s ease-in-out; }
1073
+ .animate-heartbeat { animation: heartbeat 0.5s ease-in-out; }
1074
+
1075
+ .clip-path-hexagon { clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); }
1076
+ .clip-path-octagon { clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%); }
1077
+ .clip-path-shield { clip-path: polygon(50% 0%, 100% 0%, 100% 75%, 50% 100%, 0% 75%, 0% 0%); }
1078
+
1079
+ .ease-elastic { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
1080
+ .ease-bounce { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
1081
+
518
1082
  .scrollbar-none {
519
1083
  -ms-overflow-style: none;
520
1084
  scrollbar-width: none;
521
1085
  }
522
-
523
- .scrollbar-none::-webkit-scrollbar {
524
- display: none;
525
- }
526
-
527
- [role="menu"],
528
- [role="listbox"] {
1086
+ .scrollbar-none::-webkit-scrollbar { display: none; }
1087
+
1088
+ [role="menu"], [role="listbox"] {
529
1089
  -ms-overflow-style: none;
530
1090
  scrollbar-width: none;
531
1091
  }
532
-
533
1092
  [role="menu"]::-webkit-scrollbar,
534
- [role="listbox"]::-webkit-scrollbar {
535
- display: none;
536
- }
537
-
1093
+ [role="listbox"]::-webkit-scrollbar { display: none; }
1094
+
538
1095
  [contenteditable][data-placeholder]:empty:before {
539
1096
  content: attr(data-placeholder);
540
1097
  color: hsl(var(--muted-foreground));
541
1098
  opacity: 0.5;
542
1099
  pointer-events: none;
543
1100
  }
544
-
1101
+
545
1102
  [contenteditable]:focus:empty:before {
546
1103
  content: attr(data-placeholder);
547
1104
  color: hsl(var(--muted-foreground));
548
1105
  opacity: 0.3;
549
1106
  }
1107
+
1108
+ input[type="number"] {
1109
+ -moz-appearance: textfield;
1110
+ -webkit-appearance: none;
1111
+ appearance: none;
1112
+ }
1113
+ input[type="number"]::-webkit-inner-spin-button,
1114
+ input[type="number"]::-webkit-outer-spin-button {
1115
+ -webkit-appearance: none;
1116
+ margin: 0;
1117
+ }
1118
+ }
1119
+
1120
+ @media (prefers-reduced-motion: reduce) {
1121
+ *, *::before, *::after {
1122
+ animation-duration: 0.01ms !important;
1123
+ animation-iteration-count: 1 !important;
1124
+ }
550
1125
  }`;
551
1126
 
552
1127
  // CSS for Tailwind v3
@@ -651,10 +1226,8 @@ const getRelativePathToNodeModules = (fromFile) => {
651
1226
  const nodeModulesPath = path.join(R, "node_modules");
652
1227
  let relativePath = path.relative(fromDir, nodeModulesPath);
653
1228
 
654
- // Convert Windows backslashes to forward slashes for consistency
655
1229
  relativePath = relativePath.replace(/\\/g, '/');
656
1230
 
657
- // If in same directory, use ./
658
1231
  if (!relativePath) {
659
1232
  relativePath = '.';
660
1233
  } else if (!relativePath.startsWith('.')) {
@@ -665,7 +1238,7 @@ const getRelativePathToNodeModules = (fromFile) => {
665
1238
  };
666
1239
 
667
1240
  // ----------------------------------------------
668
- // Update Tailwind config for v3 (auto-update content + manual hints for theme)
1241
+ // Update Tailwind config for v3
669
1242
  // ----------------------------------------------
670
1243
  const updateTailwindConfig = (cssFilePath) => {
671
1244
  const configFiles = [
@@ -686,171 +1259,325 @@ const updateTailwindConfig = (cssFilePath) => {
686
1259
 
687
1260
  if (!configPath) {
688
1261
  console.warn("⚠️ Warning: Could not find Tailwind config file");
689
- console.log("\n📝 Please manually create tailwind.config.js and add:");
690
- console.log(`
691
- export default {
692
- content: [
693
- "./src/**/*.{js,jsx,ts,tsx}",
694
- "./node_modules/saha-ui/dist/**/*.js", // ← Required for saha-ui
695
- ],
696
- // ... rest of your config
697
- };
698
- `);
1262
+ console.log("\n📝 Please manually create tailwind.config.js and add saha-ui content path.");
699
1263
  return;
700
1264
  }
701
1265
 
702
- // Calculate relative path from config file to node_modules
703
1266
  const relativePathToNodeModules = getRelativePathToNodeModules(configPath);
704
1267
  const sahaUIContentPath = `${relativePathToNodeModules}/saha-ui/dist/**/*.js`;
705
1268
 
706
1269
  let config = rd(configPath);
707
1270
  const contentPattern = /node_modules\/saha-ui\/dist\/\*\*\/\*\.js/;
708
1271
 
709
- // Check if saha-ui content path already exists
710
1272
  if (contentPattern.test(config)) {
711
1273
  console.log("✅ Tailwind config already includes saha-ui content path");
712
1274
  } else {
713
- // Try to automatically add the content path
714
1275
  const contentArrayRegex = /content\s*:\s*\[([\s\S]*?)\]/;
715
1276
  const match = config.match(contentArrayRegex);
716
1277
 
717
1278
  if (match) {
718
1279
  const currentContent = match[ 1 ];
719
1280
  const newContentPath = `\n "${sahaUIContentPath}",`;
720
-
721
- // Add before the closing bracket
722
1281
  const updatedContent = currentContent.trimEnd() + newContentPath;
723
1282
  config = config.replace(contentArrayRegex, `content: [${updatedContent}\n ]`);
724
1283
 
725
1284
  wr(configPath, config);
726
1285
  console.log(`✅ Added saha-ui content path to ${path.relative(R, configPath)}`);
727
- console.log(` Path: "${sahaUIContentPath}"`);
728
1286
  } else {
729
1287
  console.warn("⚠️ Could not automatically update content array");
730
- console.log("\n📝 Please manually add this to your tailwind.config content array:");
731
- console.log(` "${sahaUIContentPath}"`);
1288
+ console.log(`\n📝 Please manually add: "${sahaUIContentPath}"`);
732
1289
  }
733
1290
  }
734
-
735
- // Theme extension hints (manual for now to avoid breaking existing configs)
736
- if (config.includes("theme:") && config.includes("extend:")) {
737
- console.log("\nℹ️ Tailwind config already has theme.extend");
738
- console.log("📝 If you haven't already, add these to your tailwind.config theme.extend:");
739
- console.log(`
740
- colors: {
741
- border: "hsl(var(--border))",
742
- input: "hsl(var(--input))",
743
- ring: "hsl(var(--ring))",
744
- background: "hsl(var(--background))",
745
- foreground: "hsl(var(--foreground))",
746
- primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))" },
747
- secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))" },
748
- muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))" },
749
- accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))" },
750
- destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))" },
751
- success: { DEFAULT: "hsl(var(--success))", foreground: "hsl(var(--success-foreground))" },
752
- warning: { DEFAULT: "hsl(var(--warning))", foreground: "hsl(var(--warning-foreground))" },
753
- error: { DEFAULT: "hsl(var(--error))", foreground: "hsl(var(--error-foreground))" },
754
- info: { DEFAULT: "hsl(var(--info))", foreground: "hsl(var(--info-foreground))" },
755
- card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))" },
756
- popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))" },
757
- chart: { 1: "hsl(var(--chart-1))", 2: "hsl(var(--chart-2))", 3: "hsl(var(--chart-3))", 4: "hsl(var(--chart-4))", 5: "hsl(var(--chart-5))" },
758
- },
759
- borderRadius: {
760
- lg: "var(--radius)",
761
- md: "calc(var(--radius) - 2px)",
762
- sm: "calc(var(--radius) - 4px)",
763
- },
764
- keyframes: {
765
- "progress-stripes": { "0%": { backgroundPosition: "0 0" }, "100%": { backgroundPosition: "1.5rem 0" } },
766
- "progress-indeterminate": { "0%": { left: "-40%", transform: "scaleX(0.6)" }, "50%": { transform: "scaleX(1)" }, "100%": { left: "100%", transform: "scaleX(0.6)" } },
767
- "progress-shimmer": { "0%": { transform: "translateX(-100%) scaleX(0)", opacity: "0" }, "10%": { opacity: "1" }, "50%": { transform: "translateX(0%) scaleX(1)" }, "90%": { opacity: "1" }, "100%": { transform: "translateX(100%) scaleX(0)", opacity: "0" } },
768
- "progress-glow-pulse": { "0%, 100%": { filter: "brightness(1) saturate(1)" }, "50%": { filter: "brightness(1.15) saturate(1.2)" } },
769
- "collapsible-down": { from: { height: "0", opacity: "0" }, to: { height: "var(--radix-collapsible-content-height)", opacity: "1" } },
770
- "collapsible-up": { from: { height: "var(--radix-collapsible-content-height)", opacity: "1" }, to: { height: "0", opacity: "0" } },
771
- },
772
- animation: {
773
- "progress-stripes": "progress-stripes 1s linear infinite",
774
- "progress-indeterminate": "progress-indeterminate 1.5s ease-in-out infinite",
775
- "progress-shimmer": "progress-shimmer 2s ease-in-out infinite",
776
- "progress-glow-pulse": "progress-glow-pulse 2s ease-in-out infinite",
777
- "collapsible-down": "collapsible-down 0.2s ease-out",
778
- "collapsible-up": "collapsible-up 0.2s ease-out",
779
- },
780
- `);
781
- }
782
1291
  };
783
1292
 
784
1293
  // ----------------------------------------------
785
- // Inject
1294
+ // Smart Inject with Merge/Replace Options
786
1295
  // ----------------------------------------------
787
- const inject = (f, tailwindInfo) => {
1296
+ const inject = async (f, tailwindInfo) => {
788
1297
  const ex = fE(f);
789
1298
  const cur = ex ? rd(f) : "";
790
1299
 
791
- const hasTailwindV4Import = /@import\s+["']tailwindcss["'];?/.test(cur);
792
- const hasTailwindV3Import = cur.includes("@tailwind");
793
- const hasTailwindImport = hasTailwindV4Import || hasTailwindV3Import;
794
-
795
- // For Tailwind v4, always check and add @source if missing
796
- if (hasTailwindV4Import) {
797
- const relativePathToNodeModules = getRelativePathToNodeModules(f);
798
- const sahaUISourcePath = `${relativePathToNodeModules}/saha-ui/dist/**/*.js`;
799
- const sourcePattern = /source\s+["'][^"']*saha-ui[^"']*["']/;
1300
+ // Detect existing structures
1301
+ const structures = detectExistingStructures(cur);
1302
+ const hasExistingSetup = structures.hasRoot || structures.hasDark || structures.hasLayerBase;
800
1303
 
801
- if (!sourcePattern.test(cur)) {
802
- // Add @source to the @import line
803
- const updatedContent = cur.replace(
804
- /@import\s+["']tailwindcss["'];?/,
805
- `@import "tailwindcss";
806
- @source "${sahaUISourcePath}";`
807
- );
808
- wr(f, updatedContent);
809
- console.log(`\n✅ Added @source "${sahaUISourcePath}" to Tailwind v4 import`);
810
- } else {
811
- console.log("✅ Tailwind v4 @source already includes saha-ui");
812
- }
813
- }
814
-
815
- // Now check if CSS content is already injected
1304
+ // Check if already injected
816
1305
  if (cur.includes(M)) {
817
1306
  console.log(`✅ saha-ui: CSS already injected in ${path.relative(R, f)}`);
818
1307
  return;
819
1308
  }
820
1309
 
1310
+ let mode = "fresh"; // fresh, replace, merge
1311
+
1312
+ // If existing setup detected, ask user what to do
1313
+ if (hasExistingSetup) {
1314
+ console.log("\n⚠️ Existing CSS setup detected in your file:");
1315
+ if (structures.hasRoot) console.log(" • :root variables found");
1316
+ if (structures.hasDark) console.log(" • .dark variables found");
1317
+ if (structures.hasLayerBase) console.log(" • @layer base found");
1318
+ if (structures.hasLayerComponents) console.log(" • @layer components found");
1319
+ if (structures.hasLayerUtilities) console.log(" • @layer utilities found");
1320
+ if (structures.hasKeyframes) console.log(" • @keyframes found");
1321
+ if (structures.hasGlassClass) console.log(" • .glass class found");
1322
+
1323
+ mode = await selectPrompt(
1324
+ "\nHow would you like to handle existing styles?",
1325
+ [
1326
+ { label: "Replace all - Overwrite existing styles with saha-ui defaults", value: "replace" },
1327
+ { label: "Merge - Keep your values, add missing saha-ui variables", value: "merge" },
1328
+ { label: "Skip - Don't modify existing styles, only add new sections", value: "skip" },
1329
+ ]
1330
+ );
1331
+
1332
+ console.log(`\n📝 Selected mode: ${mode}\n`);
1333
+ }
1334
+
821
1335
  const CSS = tailwindInfo.major >= 4 ? CSS_V4 : CSS_V3;
822
1336
 
823
- let cssToInject = CSS;
824
- if (hasTailwindImport) {
825
- if (hasTailwindV4Import) {
826
- cssToInject = CSS.replace(/^@import\s+["']tailwindcss["'][^;]*;?\n*/, "").trim();
827
- } else if (hasTailwindV3Import) {
828
- cssToInject = CSS.replace(/@tailwind\s+(base|components|utilities);?\n*/g, "").trim();
1337
+ // Handle based on mode
1338
+ if (mode === "fresh" || mode === "replace") {
1339
+ // Full replacement or fresh install
1340
+ let out = "";
1341
+
1342
+ if (mode === "replace") {
1343
+ // Remove existing blocks that we'll replace
1344
+ let cleanedCss = cur;
1345
+
1346
+ // Remove :root block
1347
+ const rootBlock = extractBlock(cleanedCss, /:root\s*\{/);
1348
+ if (rootBlock) {
1349
+ cleanedCss = cleanedCss.substring(0, rootBlock.startIndex) + cleanedCss.substring(rootBlock.endIndex);
1350
+ }
1351
+
1352
+ // Remove .dark block
1353
+ const darkBlock = extractBlock(cleanedCss, /\.dark\s*\{/);
1354
+ if (darkBlock) {
1355
+ cleanedCss = cleanedCss.substring(0, darkBlock.startIndex) + cleanedCss.substring(darkBlock.endIndex);
1356
+ }
1357
+
1358
+ // Remove @theme inline block (v4)
1359
+ const themeBlock = extractBlock(cleanedCss, /@theme\s+inline\s*\{/);
1360
+ if (themeBlock) {
1361
+ cleanedCss = cleanedCss.substring(0, themeBlock.startIndex) + cleanedCss.substring(themeBlock.endIndex);
1362
+ }
1363
+
1364
+ // Remove @layer base block
1365
+ const baseBlock = extractBlock(cleanedCss, /@layer\s+base\s*\{/);
1366
+ if (baseBlock) {
1367
+ cleanedCss = cleanedCss.substring(0, baseBlock.startIndex) + cleanedCss.substring(baseBlock.endIndex);
1368
+ }
1369
+
1370
+ // Remove @layer components block
1371
+ const componentsBlock = extractBlock(cleanedCss, /@layer\s+components\s*\{/);
1372
+ if (componentsBlock) {
1373
+ cleanedCss = cleanedCss.substring(0, componentsBlock.startIndex) + cleanedCss.substring(componentsBlock.endIndex);
1374
+ }
1375
+
1376
+ // Remove @layer utilities block
1377
+ const utilitiesBlock = extractBlock(cleanedCss, /@layer\s+utilities\s*\{/);
1378
+ if (utilitiesBlock) {
1379
+ cleanedCss = cleanedCss.substring(0, utilitiesBlock.startIndex) + cleanedCss.substring(utilitiesBlock.endIndex);
1380
+ }
1381
+
1382
+ // Remove @custom-variant dark
1383
+ cleanedCss = cleanedCss.replace(/@custom-variant\s+dark\s*\([^)]*\)\s*;?\n*/g, "");
1384
+
1385
+ // Clean up multiple empty lines
1386
+ cleanedCss = cleanedCss.replace(/\n{3,}/g, "\n\n").trim();
1387
+
1388
+ // Now inject the CSS
1389
+ let cssToInject = CSS;
1390
+
1391
+ // Check if tailwind import/directives exist
1392
+ if (structures.hasTailwindImport) {
1393
+ cssToInject = CSS.replace(/^@import\s+["']tailwindcss["'][^;]*;?\n*/, "").trim();
1394
+ out = cleanedCss.replace(/@import\s+["']tailwindcss["'][^;]*;/, (m) => `${m}\n${M}\n${cssToInject}`);
1395
+ } else if (structures.hasTailwindDirectives) {
1396
+ cssToInject = CSS.replace(/@tailwind\s+(base|components|utilities);?\n*/g, "").trim();
1397
+ const tailwindDirectives = cleanedCss.match(/@tailwind\s+(base|components|utilities);?\n*/g);
1398
+ if (tailwindDirectives) {
1399
+ const lastDirective = tailwindDirectives[ tailwindDirectives.length - 1 ];
1400
+ const lastIndex = cleanedCss.lastIndexOf(lastDirective);
1401
+ const beforeDirective = cleanedCss.substring(0, lastIndex + lastDirective.length);
1402
+ const afterDirective = cleanedCss.substring(lastIndex + lastDirective.length);
1403
+ out = `${beforeDirective}\n${M}\n${cssToInject}\n${afterDirective}`;
1404
+ } else {
1405
+ out = `${M}\n${cssToInject}\n\n${cleanedCss}`;
1406
+ }
1407
+ } else {
1408
+ out = cleanedCss ? `${cleanedCss}\n\n${M}\n${CSS}` : `${M}\n${CSS}`;
1409
+ }
1410
+
1411
+ console.log("✅ Replaced existing styles with saha-ui defaults");
1412
+ } else {
1413
+ // Fresh install
1414
+ if (structures.hasTailwindImport) {
1415
+ const cssToInject = CSS.replace(/^@import\s+["']tailwindcss["'][^;]*;?\n*/, "").trim();
1416
+ out = cur.replace(/@import\s+["']tailwindcss["'][^;]*;/, (m) => `${m}\n${M}\n${cssToInject}`);
1417
+ } else if (structures.hasTailwindDirectives) {
1418
+ const cssToInject = CSS.replace(/@tailwind\s+(base|components|utilities);?\n*/g, "").trim();
1419
+ const tailwindDirectives = cur.match(/@tailwind\s+(base|components|utilities);?\n*/g);
1420
+ if (tailwindDirectives) {
1421
+ const lastDirective = tailwindDirectives[ tailwindDirectives.length - 1 ];
1422
+ const lastIndex = cur.lastIndexOf(lastDirective);
1423
+ out = `${cur.substring(0, lastIndex + lastDirective.length)}\n${M}\n${cssToInject}\n${cur.substring(lastIndex + lastDirective.length)}`;
1424
+ } else {
1425
+ out = `${M}\n${CSS}\n\n${cur}`;
1426
+ }
1427
+ } else {
1428
+ out = `${M}\n${CSS}\n\n${cur}`;
1429
+ }
1430
+ }
1431
+
1432
+ wr(f, out);
1433
+ } else if (mode === "merge") {
1434
+ // Merge mode - add missing variables
1435
+ let updatedCss = cur;
1436
+ let totalAdded = 0;
1437
+
1438
+ // Merge :root variables
1439
+ const rootResult = updateRootBlock(updatedCss, SAHA_UI_ROOT_VARIABLES, "merge");
1440
+ updatedCss = rootResult.css;
1441
+ if (rootResult.added > 0) {
1442
+ console.log(` ✅ Added ${rootResult.added} missing :root variables`);
1443
+ totalAdded += rootResult.added;
1444
+ }
1445
+
1446
+ // Merge .dark variables
1447
+ const darkResult = updateDarkBlock(updatedCss, SAHA_UI_DARK_VARIABLES, "merge");
1448
+ updatedCss = darkResult.css;
1449
+ if (darkResult.added > 0) {
1450
+ console.log(` ✅ Added ${darkResult.added} missing .dark variables`);
1451
+ totalAdded += darkResult.added;
1452
+ }
1453
+
1454
+ // Merge keyframes
1455
+ const keyframeResult = mergeKeyframes(updatedCss, SAHA_UI_KEYFRAMES);
1456
+ updatedCss = keyframeResult.css;
1457
+ if (keyframeResult.added > 0) {
1458
+ console.log(` ✅ Added ${keyframeResult.added} missing @keyframes`);
1459
+ totalAdded += keyframeResult.added;
1460
+ }
1461
+
1462
+ // Add @custom-variant dark if missing (v4)
1463
+ if (tailwindInfo.major >= 4 && !structures.hasCustomVariant) {
1464
+ if (structures.hasTailwindImport) {
1465
+ updatedCss = updatedCss.replace(
1466
+ /@import\s+["']tailwindcss["'][^;]*;/,
1467
+ (m) => `${m}\n\n@custom-variant dark (&:is(.dark *));`
1468
+ );
1469
+ console.log(` ✅ Added @custom-variant dark`);
1470
+ }
1471
+ }
1472
+
1473
+ // Add @theme inline if missing (v4)
1474
+ if (tailwindInfo.major >= 4 && !structures.hasThemeInline) {
1475
+ const themeInline = `
1476
+ @theme inline {
1477
+ --radius-sm: calc(var(--radius) - 4px);
1478
+ --radius-md: calc(var(--radius) - 2px);
1479
+ --radius-lg: var(--radius);
1480
+ --radius-xl: calc(var(--radius) + 4px);
1481
+
1482
+ --color-background: var(--background);
1483
+ --color-foreground: var(--foreground);
1484
+ --color-card: var(--card);
1485
+ --color-card-foreground: var(--card-foreground);
1486
+ --color-popover: var(--popover);
1487
+ --color-popover-foreground: var(--popover-foreground);
1488
+ --color-primary: var(--primary);
1489
+ --color-primary-foreground: var(--primary-foreground);
1490
+ --color-secondary: var(--secondary);
1491
+ --color-secondary-foreground: var(--secondary-foreground);
1492
+ --color-muted: var(--muted);
1493
+ --color-muted-foreground: var(--muted-foreground);
1494
+ --color-accent: var(--accent);
1495
+ --color-accent-foreground: var(--accent-foreground);
1496
+ --color-destructive: var(--destructive);
1497
+ --color-destructive-foreground: var(--destructive-foreground);
1498
+ --color-border: var(--border);
1499
+ --color-input: var(--input);
1500
+ --color-ring: var(--ring);
1501
+ --color-chart-1: var(--chart-1);
1502
+ --color-chart-2: var(--chart-2);
1503
+ --color-chart-3: var(--chart-3);
1504
+ --color-chart-4: var(--chart-4);
1505
+ --color-chart-5: var(--chart-5);
1506
+ --color-success: var(--success);
1507
+ --color-success-foreground: var(--success-foreground);
1508
+ --color-warning: var(--warning);
1509
+ --color-warning-foreground: var(--warning-foreground);
1510
+ --color-error: var(--error);
1511
+ --color-error-foreground: var(--error-foreground);
1512
+ --color-info: var(--info);
1513
+ --color-info-foreground: var(--info-foreground);
1514
+ }`;
1515
+
1516
+ // Insert after @custom-variant or @import
1517
+ if (updatedCss.includes("@custom-variant dark")) {
1518
+ updatedCss = updatedCss.replace(
1519
+ /@custom-variant\s+dark\s*\([^)]*\)\s*;?/,
1520
+ (m) => `${m}\n${themeInline}`
1521
+ );
1522
+ } else if (structures.hasTailwindImport) {
1523
+ updatedCss = updatedCss.replace(
1524
+ /@import\s+["']tailwindcss["'][^;]*;/,
1525
+ (m) => `${m}\n${themeInline}`
1526
+ );
1527
+ }
1528
+ console.log(` ✅ Added @theme inline block`);
829
1529
  }
830
- }
831
1530
 
832
- let out;
833
- if (hasTailwindV4Import) {
834
- // Re-read file in case @source was just added
835
- const currentContent = rd(f);
836
- out = currentContent.replace(/@import\s+["']tailwindcss["'][^;]*;/, (m) => `${m}\n${M}\n${cssToInject}`);
837
- } else if (hasTailwindV3Import) {
838
- const tailwindDirectives = cur.match(/@tailwind\s+(base|components|utilities);?\n*/g);
839
- if (tailwindDirectives) {
840
- const lastDirective = tailwindDirectives[ tailwindDirectives.length - 1 ];
841
- const lastIndex = cur.lastIndexOf(lastDirective);
842
- const beforeDirective = cur.substring(0, lastIndex + lastDirective.length);
843
- const afterDirective = cur.substring(lastIndex + lastDirective.length);
844
- out = `${beforeDirective}\n${M}\n${cssToInject}\n${afterDirective}`;
1531
+ // Add marker if not present
1532
+ if (!updatedCss.includes(M)) {
1533
+ if (structures.hasTailwindImport) {
1534
+ updatedCss = updatedCss.replace(/@import\s+["']tailwindcss["'][^;]*;/, (m) => `${m}\n${M}`);
1535
+ } else {
1536
+ updatedCss = `${M}\n${updatedCss}`;
1537
+ }
1538
+ }
1539
+
1540
+ if (totalAdded > 0) {
1541
+ console.log(`\n✅ Merged ${totalAdded} total items into your CSS`);
845
1542
  } else {
846
- out = `${M}\n${cssToInject}\n\n${cur}`;
1543
+ console.log("\n✅ Your CSS already has all required saha-ui variables");
1544
+ }
1545
+
1546
+ wr(f, updatedCss);
1547
+ } else if (mode === "skip") {
1548
+ // Skip mode - only add marker and @source for v4
1549
+ let updatedCss = cur;
1550
+
1551
+ if (!updatedCss.includes(M)) {
1552
+ if (structures.hasTailwindImport) {
1553
+ updatedCss = updatedCss.replace(/@import\s+["']tailwindcss["'][^;]*;/, (m) => `${m}\n${M}`);
1554
+ } else {
1555
+ updatedCss = `${M}\n${updatedCss}`;
1556
+ }
1557
+ }
1558
+
1559
+ console.log("✅ Skipped style modifications, only added saha-ui marker");
1560
+ wr(f, updatedCss);
1561
+ }
1562
+
1563
+ // Add @source for Tailwind v4
1564
+ const updatedContent = rd(f);
1565
+ if (tailwindInfo.major >= 4 && structures.hasTailwindImport) {
1566
+ const relativePathToNodeModules = getRelativePathToNodeModules(f);
1567
+ const sahaUISourcePath = `${relativePathToNodeModules}/saha-ui/dist/**/*.js`;
1568
+ const sourcePattern = /source\s+["'][^"']*saha-ui[^"']*["']/;
1569
+
1570
+ if (!sourcePattern.test(updatedContent)) {
1571
+ const withSource = updatedContent.replace(
1572
+ /@import\s+["']tailwindcss["'];?/,
1573
+ `@import "tailwindcss";\n@source "${sahaUISourcePath}";`
1574
+ );
1575
+ wr(f, withSource);
1576
+ console.log(`✅ Added @source "${sahaUISourcePath}" for Tailwind v4`);
847
1577
  }
848
- } else {
849
- out = `${M}\n${CSS}\n\n${cur}`;
850
1578
  }
851
1579
 
852
- wr(f, out);
853
- console.log(`\n✅ saha-ui: Injected CSS into ${path.relative(R, f)} (${F})`);
1580
+ console.log(`\n✅ saha-ui: CSS processed in ${path.relative(R, f)} (${F})`);
854
1581
  console.log(`📦 Using Tailwind v${tailwindInfo.major} configuration`);
855
1582
 
856
1583
  if (tailwindInfo.major < 4) {
@@ -873,7 +1600,7 @@ const inject = (f, tailwindInfo) => {
873
1600
  // ----------------------------------------------
874
1601
  // Main
875
1602
  // ----------------------------------------------
876
- const run = () => {
1603
+ const run = async () => {
877
1604
  if (c !== "init") {
878
1605
  console.log("Usage: npx saha-ui init");
879
1606
  return;
@@ -898,8 +1625,8 @@ const run = () => {
898
1625
  ensureSahaUIInstalled();
899
1626
  installSahaUIDeps();
900
1627
 
901
- // 5) Inject CSS (v4: just CSS; v3: CSS + config hints)
902
- inject(pick(), tailwindInfo);
1628
+ // 5) Inject CSS with smart merge/replace options
1629
+ await inject(pick(), tailwindInfo);
903
1630
 
904
1631
  console.log("\n✨ Done!\n");
905
1632
  };