@gemini-designer/mcp-server 0.1.2 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/catalog.d.ts.map +1 -1
- package/dist/components/catalog.js +10 -4
- package/dist/components/catalog.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +11 -6
- package/dist/config/index.js.map +1 -1
- package/dist/context/builder.d.ts.map +1 -1
- package/dist/context/builder.js.map +1 -1
- package/dist/context/filter.d.ts.map +1 -1
- package/dist/context/filter.js +5 -1
- package/dist/context/filter.js.map +1 -1
- package/dist/context/grounding.d.ts.map +1 -1
- package/dist/context/grounding.js +7 -3
- package/dist/context/grounding.js.map +1 -1
- package/dist/context/guards.d.ts.map +1 -1
- package/dist/context/guards.js +53 -0
- package/dist/context/guards.js.map +1 -1
- package/dist/context/repo-hints.js.map +1 -1
- package/dist/context/styling-detector.d.ts +24 -0
- package/dist/context/styling-detector.d.ts.map +1 -0
- package/dist/context/styling-detector.js +337 -0
- package/dist/context/styling-detector.js.map +1 -0
- package/dist/design/principles.js.map +1 -1
- package/dist/generation/gemini-client.d.ts.map +1 -1
- package/dist/generation/gemini-client.js.map +1 -1
- package/dist/generation/litellm-client.d.ts.map +1 -1
- package/dist/generation/litellm-client.js +14 -7
- package/dist/generation/litellm-client.js.map +1 -1
- package/dist/generation/remote-client.d.ts +10 -5
- package/dist/generation/remote-client.d.ts.map +1 -1
- package/dist/generation/remote-client.js +13 -2
- package/dist/generation/remote-client.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/output/file-writer.d.ts.map +1 -1
- package/dist/output/file-writer.js +4 -4
- package/dist/output/file-writer.js.map +1 -1
- package/dist/output/formatter.d.ts.map +1 -1
- package/dist/output/formatter.js +5 -2
- package/dist/output/formatter.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/stack/detect.d.ts.map +1 -1
- package/dist/stack/detect.js +42 -9
- package/dist/stack/detect.js.map +1 -1
- package/dist/tokens/sync.d.ts.map +1 -1
- package/dist/tokens/sync.js +22 -5
- package/dist/tokens/sync.js.map +1 -1
- package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -1
- package/dist/tools/analyze-screenshot-ui.js +5 -5
- package/dist/tools/analyze-screenshot-ui.js.map +1 -1
- package/dist/tools/analyze-tokens.d.ts.map +1 -1
- package/dist/tools/analyze-tokens.js +3 -1
- package/dist/tools/analyze-tokens.js.map +1 -1
- package/dist/tools/catalog-components.d.ts.map +1 -1
- package/dist/tools/catalog-components.js +1 -4
- package/dist/tools/catalog-components.js.map +1 -1
- package/dist/tools/create-ui.d.ts +3 -0
- package/dist/tools/create-ui.d.ts.map +1 -1
- package/dist/tools/create-ui.js +203 -75
- package/dist/tools/create-ui.js.map +1 -1
- package/dist/tools/detect-ui-stack.js.map +1 -1
- package/dist/tools/generate-component-variants.d.ts.map +1 -1
- package/dist/tools/generate-component-variants.js +15 -4
- package/dist/tools/generate-component-variants.js.map +1 -1
- package/dist/tools/generate-vibes.d.ts.map +1 -1
- package/dist/tools/generate-vibes.js +7 -3
- package/dist/tools/generate-vibes.js.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/modify-ui.d.ts.map +1 -1
- package/dist/tools/modify-ui.js +7 -2
- package/dist/tools/modify-ui.js.map +1 -1
- package/dist/tools/scaffold-project.d.ts.map +1 -1
- package/dist/tools/scaffold-project.js +3 -1
- package/dist/tools/scaffold-project.js.map +1 -1
- package/dist/tools/snippet-ui.d.ts +3 -1
- package/dist/tools/snippet-ui.d.ts.map +1 -1
- package/dist/tools/snippet-ui.js +219 -88
- package/dist/tools/snippet-ui.js.map +1 -1
- package/dist/tools/sync-design-tokens.d.ts.map +1 -1
- package/dist/tools/sync-design-tokens.js +26 -11
- package/dist/tools/sync-design-tokens.js.map +1 -1
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +55 -55
- package/src/__tests__/builder.test.ts +19 -19
- package/src/__tests__/config.test.ts +63 -31
- package/src/__tests__/filter.test.ts +98 -92
- package/src/__tests__/remote-client.test.ts +179 -0
- package/src/components/catalog.ts +170 -166
- package/src/config/index.ts +185 -177
- package/src/context/builder.ts +157 -157
- package/src/context/filter.ts +110 -104
- package/src/context/grounding.ts +143 -129
- package/src/context/guards.ts +97 -38
- package/src/context/repo-hints.ts +24 -24
- package/src/context/styling-detector.ts +460 -0
- package/src/design/principles.ts +14 -14
- package/src/generation/gemini-client.ts +53 -56
- package/src/generation/litellm-client.ts +102 -86
- package/src/generation/remote-client.ts +100 -77
- package/src/index.ts +16 -16
- package/src/output/file-writer.ts +123 -123
- package/src/output/formatter.ts +139 -132
- package/src/server.ts +12 -11
- package/src/stack/detect.ts +226 -175
- package/src/tokens/sync.ts +189 -155
- package/src/tools/analyze-screenshot-ui.ts +89 -88
- package/src/tools/analyze-tokens.ts +80 -78
- package/src/tools/catalog-components.ts +68 -68
- package/src/tools/create-ui.ts +295 -142
- package/src/tools/detect-ui-stack.ts +36 -36
- package/src/tools/generate-component-variants.ts +155 -135
- package/src/tools/generate-vibes.ts +121 -117
- package/src/tools/index.ts +14 -14
- package/src/tools/modify-ui.ts +170 -165
- package/src/tools/scaffold-project.ts +68 -66
- package/src/tools/snippet-ui.ts +323 -172
- package/src/tools/sync-design-tokens.ts +217 -195
- package/src/utils/walk.ts +47 -45
- package/src/version.ts +6 -0
- package/tsconfig.json +23 -33
- package/vitest.config.ts +10 -10
- package/.prettierrc +0 -9
- package/eslint.config.js +0 -37
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styling Approach Detection
|
|
3
|
+
*
|
|
4
|
+
* Auto-detects the styling approach used in a project by scanning for
|
|
5
|
+
* config files, package.json dependencies, and file patterns.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
|
|
11
|
+
export type StylingApproach =
|
|
12
|
+
| 'tailwind'
|
|
13
|
+
| 'css-modules'
|
|
14
|
+
| 'styled-components'
|
|
15
|
+
| 'emotion'
|
|
16
|
+
| 'scss'
|
|
17
|
+
| 'vanilla-extract'
|
|
18
|
+
| 'panda-css'
|
|
19
|
+
| 'uno-css'
|
|
20
|
+
| 'stylex'
|
|
21
|
+
| 'css-in-js'
|
|
22
|
+
| 'vanilla-css';
|
|
23
|
+
|
|
24
|
+
export interface StylingInfo {
|
|
25
|
+
approach: StylingApproach;
|
|
26
|
+
confidence: 'high' | 'medium' | 'low';
|
|
27
|
+
detectedFrom: string;
|
|
28
|
+
fileExtension: string;
|
|
29
|
+
importStatement: string;
|
|
30
|
+
usage: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PackageJson {
|
|
34
|
+
dependencies?: Record<string, string>;
|
|
35
|
+
devDependencies?: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Default styling info when nothing is detected
|
|
40
|
+
*/
|
|
41
|
+
const DEFAULT_STYLING: StylingInfo = {
|
|
42
|
+
approach: 'css-modules',
|
|
43
|
+
confidence: 'low',
|
|
44
|
+
detectedFrom: 'default fallback',
|
|
45
|
+
fileExtension: '.module.css',
|
|
46
|
+
importStatement: "import styles from './Component.module.css';",
|
|
47
|
+
usage: 'Use className={styles.className} pattern',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Styling approach configurations
|
|
52
|
+
*/
|
|
53
|
+
const STYLING_CONFIGS: Record<StylingApproach, Omit<StylingInfo, 'detectedFrom' | 'confidence'>> = {
|
|
54
|
+
tailwind: {
|
|
55
|
+
approach: 'tailwind',
|
|
56
|
+
fileExtension: '',
|
|
57
|
+
importStatement: '',
|
|
58
|
+
usage: 'Use Tailwind utility classes directly in className',
|
|
59
|
+
},
|
|
60
|
+
'css-modules': {
|
|
61
|
+
approach: 'css-modules',
|
|
62
|
+
fileExtension: '.module.css',
|
|
63
|
+
importStatement: "import styles from './Component.module.css';",
|
|
64
|
+
usage: 'Use className={styles.className} pattern',
|
|
65
|
+
},
|
|
66
|
+
'styled-components': {
|
|
67
|
+
approach: 'styled-components',
|
|
68
|
+
fileExtension: '.tsx',
|
|
69
|
+
importStatement: "import styled from 'styled-components';",
|
|
70
|
+
usage: 'Use const StyledDiv = styled.div`...`; pattern',
|
|
71
|
+
},
|
|
72
|
+
emotion: {
|
|
73
|
+
approach: 'emotion',
|
|
74
|
+
fileExtension: '.tsx',
|
|
75
|
+
importStatement: "import { css } from '@emotion/react';\nimport styled from '@emotion/styled';",
|
|
76
|
+
usage: 'Use css prop or styled.div pattern',
|
|
77
|
+
},
|
|
78
|
+
scss: {
|
|
79
|
+
approach: 'scss',
|
|
80
|
+
fileExtension: '.module.scss',
|
|
81
|
+
importStatement: "import styles from './Component.module.scss';",
|
|
82
|
+
usage: 'Use className={styles.className} with SCSS features',
|
|
83
|
+
},
|
|
84
|
+
'vanilla-extract': {
|
|
85
|
+
approach: 'vanilla-extract',
|
|
86
|
+
fileExtension: '.css.ts',
|
|
87
|
+
importStatement: "import { style } from '@vanilla-extract/css';",
|
|
88
|
+
usage: 'Define styles in .css.ts file, import and use in component',
|
|
89
|
+
},
|
|
90
|
+
'panda-css': {
|
|
91
|
+
approach: 'panda-css',
|
|
92
|
+
fileExtension: '',
|
|
93
|
+
importStatement: "import { css } from '../styled-system/css';",
|
|
94
|
+
usage: 'Use css() function from generated styled-system',
|
|
95
|
+
},
|
|
96
|
+
'uno-css': {
|
|
97
|
+
approach: 'uno-css',
|
|
98
|
+
fileExtension: '',
|
|
99
|
+
importStatement: "import 'virtual:uno.css';",
|
|
100
|
+
usage: 'Use utility classes similar to Tailwind',
|
|
101
|
+
},
|
|
102
|
+
stylex: {
|
|
103
|
+
approach: 'stylex',
|
|
104
|
+
fileExtension: '.stylex.ts',
|
|
105
|
+
importStatement: "import * as stylex from '@stylexjs/stylex';",
|
|
106
|
+
usage: 'Use stylex.create() and stylex.props()',
|
|
107
|
+
},
|
|
108
|
+
'css-in-js': {
|
|
109
|
+
approach: 'css-in-js',
|
|
110
|
+
fileExtension: '.tsx',
|
|
111
|
+
importStatement: '',
|
|
112
|
+
usage: 'Define styles as objects in component file',
|
|
113
|
+
},
|
|
114
|
+
'vanilla-css': {
|
|
115
|
+
approach: 'vanilla-css',
|
|
116
|
+
fileExtension: '.css',
|
|
117
|
+
importStatement: "import './Component.css';",
|
|
118
|
+
usage: 'Use className with plain CSS selectors',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a file exists in the project root
|
|
124
|
+
*/
|
|
125
|
+
function fileExists(projectRoot: string, ...filenames: string[]): string | null {
|
|
126
|
+
for (const filename of filenames) {
|
|
127
|
+
const fullPath = path.join(projectRoot, filename);
|
|
128
|
+
if (fs.existsSync(fullPath)) {
|
|
129
|
+
return filename;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read and parse package.json
|
|
137
|
+
*/
|
|
138
|
+
function readPackageJson(projectRoot: string): PackageJson | null {
|
|
139
|
+
try {
|
|
140
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
141
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
142
|
+
const content = fs.readFileSync(pkgPath, 'utf-8');
|
|
143
|
+
return JSON.parse(content) as PackageJson;
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if a dependency exists in package.json
|
|
151
|
+
*/
|
|
152
|
+
function hasDependency(pkg: PackageJson | null, ...deps: string[]): string | null {
|
|
153
|
+
if (!pkg) return null;
|
|
154
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
155
|
+
for (const dep of deps) {
|
|
156
|
+
if (allDeps[dep]) return dep;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check for file patterns in the project
|
|
163
|
+
*/
|
|
164
|
+
function hasFilePattern(projectRoot: string, pattern: RegExp, maxDepth = 3): boolean {
|
|
165
|
+
function scan(dir: string, depth: number): boolean {
|
|
166
|
+
if (depth > maxDepth) return false;
|
|
167
|
+
try {
|
|
168
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
171
|
+
|
|
172
|
+
if (entry.isFile() && pattern.test(entry.name)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (entry.isDirectory()) {
|
|
176
|
+
if (scan(path.join(dir, entry.name), depth + 1)) return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// Ignore read errors
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return scan(projectRoot, 0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Detect styling approach from project files
|
|
189
|
+
*/
|
|
190
|
+
export function detectStylingApproach(projectRoot: string): StylingInfo {
|
|
191
|
+
const pkg = readPackageJson(projectRoot);
|
|
192
|
+
|
|
193
|
+
// 1. Check for Tailwind (highest priority - very popular)
|
|
194
|
+
const tailwindConfig = fileExists(
|
|
195
|
+
projectRoot,
|
|
196
|
+
'tailwind.config.js',
|
|
197
|
+
'tailwind.config.ts',
|
|
198
|
+
'tailwind.config.mjs',
|
|
199
|
+
'tailwind.config.cjs'
|
|
200
|
+
);
|
|
201
|
+
if (tailwindConfig) {
|
|
202
|
+
return {
|
|
203
|
+
...STYLING_CONFIGS['tailwind'],
|
|
204
|
+
confidence: 'high',
|
|
205
|
+
detectedFrom: tailwindConfig,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (hasDependency(pkg, 'tailwindcss')) {
|
|
209
|
+
return {
|
|
210
|
+
...STYLING_CONFIGS['tailwind'],
|
|
211
|
+
confidence: 'high',
|
|
212
|
+
detectedFrom: 'package.json: tailwindcss',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 2. Check for Panda CSS
|
|
217
|
+
const pandaConfig = fileExists(
|
|
218
|
+
projectRoot,
|
|
219
|
+
'panda.config.ts',
|
|
220
|
+
'panda.config.js',
|
|
221
|
+
'panda.config.mjs'
|
|
222
|
+
);
|
|
223
|
+
if (pandaConfig) {
|
|
224
|
+
return {
|
|
225
|
+
...STYLING_CONFIGS['panda-css'],
|
|
226
|
+
confidence: 'high',
|
|
227
|
+
detectedFrom: pandaConfig,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (hasDependency(pkg, '@pandacss/dev')) {
|
|
231
|
+
return {
|
|
232
|
+
...STYLING_CONFIGS['panda-css'],
|
|
233
|
+
confidence: 'high',
|
|
234
|
+
detectedFrom: 'package.json: @pandacss/dev',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 3. Check for UnoCSS
|
|
239
|
+
const unoConfig = fileExists(projectRoot, 'uno.config.ts', 'uno.config.js', 'unocss.config.ts');
|
|
240
|
+
if (unoConfig) {
|
|
241
|
+
return {
|
|
242
|
+
...STYLING_CONFIGS['uno-css'],
|
|
243
|
+
confidence: 'high',
|
|
244
|
+
detectedFrom: unoConfig,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (hasDependency(pkg, 'unocss')) {
|
|
248
|
+
return {
|
|
249
|
+
...STYLING_CONFIGS['uno-css'],
|
|
250
|
+
confidence: 'high',
|
|
251
|
+
detectedFrom: 'package.json: unocss',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 4. Check for Vanilla Extract
|
|
256
|
+
if (hasDependency(pkg, '@vanilla-extract/css')) {
|
|
257
|
+
return {
|
|
258
|
+
...STYLING_CONFIGS['vanilla-extract'],
|
|
259
|
+
confidence: 'high',
|
|
260
|
+
detectedFrom: 'package.json: @vanilla-extract/css',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (hasFilePattern(projectRoot, /\.css\.ts$/)) {
|
|
264
|
+
return {
|
|
265
|
+
...STYLING_CONFIGS['vanilla-extract'],
|
|
266
|
+
confidence: 'medium',
|
|
267
|
+
detectedFrom: 'file pattern: *.css.ts',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 5. Check for StyleX
|
|
272
|
+
if (hasDependency(pkg, '@stylexjs/stylex')) {
|
|
273
|
+
return {
|
|
274
|
+
...STYLING_CONFIGS['stylex'],
|
|
275
|
+
confidence: 'high',
|
|
276
|
+
detectedFrom: 'package.json: @stylexjs/stylex',
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 6. Check for styled-components
|
|
281
|
+
if (hasDependency(pkg, 'styled-components')) {
|
|
282
|
+
return {
|
|
283
|
+
...STYLING_CONFIGS['styled-components'],
|
|
284
|
+
confidence: 'high',
|
|
285
|
+
detectedFrom: 'package.json: styled-components',
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
if (hasFilePattern(projectRoot, /\.styled\.(ts|tsx)$/)) {
|
|
289
|
+
return {
|
|
290
|
+
...STYLING_CONFIGS['styled-components'],
|
|
291
|
+
confidence: 'medium',
|
|
292
|
+
detectedFrom: 'file pattern: *.styled.tsx',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 7. Check for Emotion
|
|
297
|
+
if (hasDependency(pkg, '@emotion/react', '@emotion/styled')) {
|
|
298
|
+
return {
|
|
299
|
+
...STYLING_CONFIGS['emotion'],
|
|
300
|
+
confidence: 'high',
|
|
301
|
+
detectedFrom: 'package.json: @emotion/*',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// 8. Check for SCSS/SASS
|
|
306
|
+
if (hasDependency(pkg, 'sass', 'node-sass')) {
|
|
307
|
+
// Check for CSS Modules with SCSS
|
|
308
|
+
if (hasFilePattern(projectRoot, /\.module\.scss$/)) {
|
|
309
|
+
return {
|
|
310
|
+
...STYLING_CONFIGS['scss'],
|
|
311
|
+
confidence: 'high',
|
|
312
|
+
detectedFrom: 'file pattern: *.module.scss',
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
...STYLING_CONFIGS['scss'],
|
|
317
|
+
confidence: 'medium',
|
|
318
|
+
detectedFrom: 'package.json: sass',
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 9. Check for CSS Modules (file pattern)
|
|
323
|
+
if (hasFilePattern(projectRoot, /\.module\.css$/)) {
|
|
324
|
+
return {
|
|
325
|
+
...STYLING_CONFIGS['css-modules'],
|
|
326
|
+
confidence: 'high',
|
|
327
|
+
detectedFrom: 'file pattern: *.module.css',
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 10. Check for Next.js (often uses CSS Modules by default)
|
|
332
|
+
const nextConfig = fileExists(projectRoot, 'next.config.js', 'next.config.ts', 'next.config.mjs');
|
|
333
|
+
if (nextConfig) {
|
|
334
|
+
return {
|
|
335
|
+
...STYLING_CONFIGS['css-modules'],
|
|
336
|
+
confidence: 'medium',
|
|
337
|
+
detectedFrom: `${nextConfig} (Next.js default)`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 11. Default to CSS Modules (modern safe default)
|
|
342
|
+
return DEFAULT_STYLING;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get styling instructions for the prompt
|
|
347
|
+
*/
|
|
348
|
+
export function getStylingInstructions(info: StylingInfo): string {
|
|
349
|
+
const lines = [
|
|
350
|
+
`## Detected Styling Approach: ${info.approach.toUpperCase()}`,
|
|
351
|
+
`Confidence: ${info.confidence}`,
|
|
352
|
+
`Detected from: ${info.detectedFrom}`,
|
|
353
|
+
'',
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
switch (info.approach) {
|
|
357
|
+
case 'tailwind':
|
|
358
|
+
lines.push(
|
|
359
|
+
'### Tailwind CSS Instructions',
|
|
360
|
+
'- Use Tailwind utility classes directly in className',
|
|
361
|
+
'- NO separate CSS file needed',
|
|
362
|
+
'- Use @apply in global.css if needed for complex reusable styles',
|
|
363
|
+
'- Example: className="flex items-center justify-between p-4 bg-gradient-to-r from-purple-500 to-pink-500"',
|
|
364
|
+
'- Use tailwind.config variants for animations'
|
|
365
|
+
);
|
|
366
|
+
break;
|
|
367
|
+
|
|
368
|
+
case 'css-modules':
|
|
369
|
+
lines.push(
|
|
370
|
+
'### CSS Modules Instructions',
|
|
371
|
+
`- Create separate ${info.fileExtension} file`,
|
|
372
|
+
`- Import: ${info.importStatement}`,
|
|
373
|
+
'- Use: className={styles.container}',
|
|
374
|
+
'- CSS classes are locally scoped by default',
|
|
375
|
+
'- Use :global() for global styles if needed'
|
|
376
|
+
);
|
|
377
|
+
break;
|
|
378
|
+
|
|
379
|
+
case 'styled-components':
|
|
380
|
+
lines.push(
|
|
381
|
+
'### styled-components Instructions',
|
|
382
|
+
`- Import: ${info.importStatement}`,
|
|
383
|
+
'- Define styled components: const Button = styled.button`...`;',
|
|
384
|
+
'- Use props: ${props => props.primary && "background: blue;"}',
|
|
385
|
+
'- NO separate CSS file needed - styles in component file'
|
|
386
|
+
);
|
|
387
|
+
break;
|
|
388
|
+
|
|
389
|
+
case 'emotion':
|
|
390
|
+
lines.push(
|
|
391
|
+
'### Emotion Instructions',
|
|
392
|
+
`- Import: ${info.importStatement}`,
|
|
393
|
+
'- Use css prop: <div css={css`color: hotpink;`}>',
|
|
394
|
+
'- Or styled API: const Button = styled.button`...`;',
|
|
395
|
+
'- NO separate CSS file needed'
|
|
396
|
+
);
|
|
397
|
+
break;
|
|
398
|
+
|
|
399
|
+
case 'scss':
|
|
400
|
+
lines.push(
|
|
401
|
+
'### SCSS/SASS Instructions',
|
|
402
|
+
`- Create separate ${info.fileExtension} file`,
|
|
403
|
+
`- Import: ${info.importStatement}`,
|
|
404
|
+
'- Use: className={styles.container}',
|
|
405
|
+
'- Can use SCSS features: nesting, variables, mixins'
|
|
406
|
+
);
|
|
407
|
+
break;
|
|
408
|
+
|
|
409
|
+
case 'vanilla-extract':
|
|
410
|
+
lines.push(
|
|
411
|
+
'### Vanilla Extract Instructions',
|
|
412
|
+
`- Create ${info.fileExtension} file for styles`,
|
|
413
|
+
`- Import: ${info.importStatement}`,
|
|
414
|
+
'- Define: export const container = style({ display: "flex" });',
|
|
415
|
+
'- Use: className={container}',
|
|
416
|
+
'- Type-safe CSS-in-JS with zero runtime'
|
|
417
|
+
);
|
|
418
|
+
break;
|
|
419
|
+
|
|
420
|
+
case 'panda-css':
|
|
421
|
+
lines.push(
|
|
422
|
+
'### Panda CSS Instructions',
|
|
423
|
+
`- Import: ${info.importStatement}`,
|
|
424
|
+
'- Use: className={css({ display: "flex", padding: "4" })}',
|
|
425
|
+
'- Utility-first like Tailwind but with type safety',
|
|
426
|
+
'- NO separate CSS file needed'
|
|
427
|
+
);
|
|
428
|
+
break;
|
|
429
|
+
|
|
430
|
+
case 'uno-css':
|
|
431
|
+
lines.push(
|
|
432
|
+
'### UnoCSS Instructions',
|
|
433
|
+
'- Use utility classes similar to Tailwind',
|
|
434
|
+
'- className="flex items-center p-4"',
|
|
435
|
+
'- NO separate CSS file needed',
|
|
436
|
+
'- Supports attributify mode if configured'
|
|
437
|
+
);
|
|
438
|
+
break;
|
|
439
|
+
|
|
440
|
+
case 'stylex':
|
|
441
|
+
lines.push(
|
|
442
|
+
'### StyleX Instructions',
|
|
443
|
+
`- Import: ${info.importStatement}`,
|
|
444
|
+
'- Define: const styles = stylex.create({ container: { display: "flex" } });',
|
|
445
|
+
'- Use: {...stylex.props(styles.container)}',
|
|
446
|
+
'- Type-safe, optimized by Meta'
|
|
447
|
+
);
|
|
448
|
+
break;
|
|
449
|
+
|
|
450
|
+
default:
|
|
451
|
+
lines.push(
|
|
452
|
+
'### Vanilla CSS Instructions',
|
|
453
|
+
`- Create separate ${info.fileExtension} file`,
|
|
454
|
+
`- Import: ${info.importStatement}`,
|
|
455
|
+
'- Use className="container" with plain CSS selectors'
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return lines.join('\n');
|
|
460
|
+
}
|
package/src/design/principles.ts
CHANGED
|
@@ -91,24 +91,24 @@ export const DESIGN_PRINCIPLES_COMPACT = `
|
|
|
91
91
|
* Font blacklist for explicit filtering
|
|
92
92
|
*/
|
|
93
93
|
export const BANNED_FONTS = [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
94
|
+
'Inter',
|
|
95
|
+
'Roboto',
|
|
96
|
+
'Arial',
|
|
97
|
+
'Open Sans',
|
|
98
|
+
'Lato',
|
|
99
|
+
'Helvetica',
|
|
100
|
+
'system-ui',
|
|
101
|
+
'sans-serif',
|
|
102
|
+
'-apple-system',
|
|
103
|
+
'BlinkMacSystemFont',
|
|
104
104
|
];
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Suggested distinctive fonts by category
|
|
108
108
|
*/
|
|
109
109
|
export const SUGGESTED_FONTS = {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
display: ['Bricolage Grotesque', 'Playfair Display', 'Outfit', 'Space Grotesk'],
|
|
111
|
+
body: ['Plus Jakarta Sans', 'Source Sans 3', 'IBM Plex Sans', 'Crimson Pro'],
|
|
112
|
+
code: ['JetBrains Mono', 'Fira Code', 'IBM Plex Mono', 'Cascadia Code'],
|
|
113
|
+
editorial: ['Newsreader', 'Playfair Display', 'Cormorant Garamond', 'Libre Baskerville'],
|
|
114
114
|
};
|
|
@@ -9,86 +9,83 @@ import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
|
9
9
|
import { Config } from '../config/index.js';
|
|
10
10
|
|
|
11
11
|
export type GeminiUserContent =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
| { text: string }
|
|
15
|
-
| { inlineData: { mimeType: string; data: string } }
|
|
16
|
-
>;
|
|
12
|
+
| string
|
|
13
|
+
| Array<{ text: string } | { inlineData: { mimeType: string; data: string } }>;
|
|
17
14
|
|
|
18
15
|
// Cache the client instance
|
|
19
16
|
let genAI: GoogleGenerativeAI | null = null;
|
|
20
17
|
|
|
21
18
|
export interface GenerateOptions {
|
|
22
|
-
|
|
19
|
+
toolName?: string;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
/**
|
|
26
23
|
* Get or create the Gemini client
|
|
27
24
|
*/
|
|
28
25
|
function getClient(config: Config): GoogleGenerativeAI {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if (!config.apiKey) {
|
|
27
|
+
throw new Error('Gemini API key not configured. Set GEMINI_API_KEY environment variable.');
|
|
28
|
+
}
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
if (!genAI) {
|
|
31
|
+
genAI = new GoogleGenerativeAI(config.apiKey);
|
|
32
|
+
}
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
return genAI;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
/**
|
|
41
38
|
* Generate content using Gemini
|
|
42
39
|
*/
|
|
43
40
|
export async function generateWithGemini(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
config: Config,
|
|
42
|
+
systemPrompt: string,
|
|
43
|
+
userPrompt: GeminiUserContent,
|
|
44
|
+
options?: GenerateOptions
|
|
48
45
|
): Promise<string> {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
46
|
+
// If remote mode, delegate to remote client
|
|
47
|
+
if (config.mode === 'remote') {
|
|
48
|
+
const { generateWithRemote } = await import('./remote-client.js');
|
|
49
|
+
return generateWithRemote(config, systemPrompt, userPrompt, options?.toolName);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If local mode is configured to use an OpenAI-compatible proxy (e.g. LiteLLM)
|
|
53
|
+
if (config.localProvider === 'litellm') {
|
|
54
|
+
const { generateWithLiteLLM } = await import('./litellm-client.js');
|
|
55
|
+
return generateWithLiteLLM(config, systemPrompt, userPrompt, options?.toolName);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const client = getClient(config);
|
|
59
|
+
|
|
60
|
+
// Use model from config (default: gemini-2.5-flash-lite)
|
|
61
|
+
const model = client.getGenerativeModel({
|
|
62
|
+
model: config.model,
|
|
63
|
+
systemInstruction: systemPrompt,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const result = await model.generateContent(userPrompt);
|
|
67
|
+
const response = result.response;
|
|
68
|
+
const text = response.text();
|
|
69
|
+
|
|
70
|
+
if (config.debug) {
|
|
71
|
+
console.error('[gemini] Tool:', options?.toolName || '(unknown)');
|
|
72
|
+
console.error('[gemini] Response length:', text.length);
|
|
73
|
+
console.error('[gemini] Usage:', response.usageMetadata);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return text;
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
/**
|
|
83
80
|
* Count tokens for a given text (useful for quota tracking)
|
|
84
81
|
*/
|
|
85
82
|
export async function countTokens(config: Config, text: string): Promise<number> {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
if (config.localProvider === 'litellm') {
|
|
84
|
+
throw new Error('countTokens is not supported when localProvider=litellm');
|
|
85
|
+
}
|
|
86
|
+
const client = getClient(config);
|
|
87
|
+
const model = client.getGenerativeModel({ model: config.model });
|
|
88
|
+
|
|
89
|
+
const result = await model.countTokens(text);
|
|
90
|
+
return result.totalTokens;
|
|
94
91
|
}
|