draply-dev 1.3.0 → 1.3.2
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/bin/cli.js +33 -32
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -100,7 +100,7 @@ function extractClassName(selector) {
|
|
|
100
100
|
// ── Call Gemini API ───────────────────────────────────────────────────────────
|
|
101
101
|
function callGemini(apiKey, prompt) {
|
|
102
102
|
return new Promise((resolve, reject) => {
|
|
103
|
-
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
|
|
103
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key=${apiKey}`;
|
|
104
104
|
const body = JSON.stringify({
|
|
105
105
|
contents: [{ parts: [{ text: prompt }] }],
|
|
106
106
|
generationConfig: { temperature: 0.1, maxOutputTokens: 8192 }
|
|
@@ -190,30 +190,40 @@ const server = http.createServer((req, res) => {
|
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Group changes by file
|
|
193
194
|
const results = [];
|
|
195
|
+
const fileChanges = new Map(); // file -> { found, allProps }
|
|
194
196
|
|
|
195
|
-
// Group changes by class
|
|
196
197
|
for (const ch of (changes || [])) {
|
|
197
198
|
if (!ch.selector || !ch.props) continue;
|
|
198
|
-
|
|
199
199
|
const className = extractClassName(ch.selector);
|
|
200
200
|
if (!className) {
|
|
201
201
|
results.push({ selector: ch.selector, ok: false, reason: 'No class name found' });
|
|
202
202
|
continue;
|
|
203
203
|
}
|
|
204
|
-
|
|
205
|
-
// Find the source file
|
|
206
204
|
const found = findFileForClass(projectRoot, className);
|
|
207
205
|
if (!found) {
|
|
208
|
-
results.push({ selector: ch.selector, ok: false, reason: `"${className}" not found in source
|
|
206
|
+
results.push({ selector: ch.selector, ok: false, reason: `"${className}" not found in source` });
|
|
209
207
|
continue;
|
|
210
208
|
}
|
|
209
|
+
if (!fileChanges.has(found.file)) {
|
|
210
|
+
fileChanges.set(found.file, { found, items: [] });
|
|
211
|
+
}
|
|
212
|
+
fileChanges.get(found.file).items.push({ selector: ch.selector, className, props: ch.props });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// One AI call per file (saves tokens & rate limit)
|
|
216
|
+
for (const [filePath, { found, items }] of fileChanges) {
|
|
217
|
+
console.log(` \x1b[36m🤖\x1b[0m AI applying ${items.length} changes → ${found.relativePath}`);
|
|
211
218
|
|
|
212
|
-
|
|
219
|
+
// Build combined prompt
|
|
220
|
+
let changesBlock = '';
|
|
221
|
+
items.forEach(item => {
|
|
222
|
+
const propsStr = Object.entries(item.props).map(([k, v]) => ` ${k}: ${v}`).join('\n');
|
|
223
|
+
changesBlock += `\nElement .${item.className}:\n${propsStr}\n`;
|
|
224
|
+
});
|
|
213
225
|
|
|
214
|
-
|
|
215
|
-
const propsStr = Object.entries(ch.props).map(([k, v]) => ` ${k}: ${v}`).join('\n');
|
|
216
|
-
const prompt = `You are a code editor. Modify the following source file to apply CSS style changes.
|
|
226
|
+
const prompt = `You are a code editor. Modify the file to apply CSS style changes.
|
|
217
227
|
|
|
218
228
|
FILE: ${found.relativePath}
|
|
219
229
|
\`\`\`
|
|
@@ -221,44 +231,35 @@ ${found.content}
|
|
|
221
231
|
\`\`\`
|
|
222
232
|
|
|
223
233
|
CHANGES TO APPLY:
|
|
224
|
-
|
|
225
|
-
${propsStr}
|
|
226
|
-
|
|
234
|
+
${changesBlock}
|
|
227
235
|
RULES:
|
|
228
|
-
- If
|
|
229
|
-
- If
|
|
230
|
-
-
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
- Otherwise
|
|
234
|
-
- Keep ALL existing code intact. Only modify what's needed
|
|
235
|
-
- Do NOT add comments
|
|
236
|
+
- If CSS/SCSS file: find each class rule and update/add properties. Add rules if missing.
|
|
237
|
+
- If JSX/TSX file: apply in the most appropriate way:
|
|
238
|
+
- Existing inline styles → update them
|
|
239
|
+
- File imports CSS → note which CSS file to edit
|
|
240
|
+
- Tailwind classes → update className with Tailwind classes
|
|
241
|
+
- Otherwise → add style prop
|
|
242
|
+
- Keep ALL existing code intact. Only modify what's needed.
|
|
243
|
+
- Do NOT add extra comments.
|
|
236
244
|
|
|
237
|
-
Return ONLY the complete modified file
|
|
245
|
+
Return ONLY the complete modified file. No markdown fences, no explanations.`;
|
|
238
246
|
|
|
239
247
|
try {
|
|
240
248
|
const result = await callGemini(cfg.apiKey, prompt);
|
|
241
|
-
|
|
242
|
-
// Clean up response — remove markdown fences if AI added them
|
|
243
249
|
let code = result.trim();
|
|
244
250
|
if (code.startsWith('```')) {
|
|
245
251
|
code = code.replace(/^```[a-z]*\n?/, '').replace(/\n?```$/, '');
|
|
246
252
|
}
|
|
247
|
-
|
|
248
|
-
// Safety: don't write empty or tiny responses
|
|
249
253
|
if (code.length < 20) {
|
|
250
|
-
results.push({ selector:
|
|
254
|
+
items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI returned invalid response' }));
|
|
251
255
|
continue;
|
|
252
256
|
}
|
|
253
|
-
|
|
254
|
-
// Write the modified file
|
|
255
257
|
fs.writeFileSync(found.file, code, 'utf8');
|
|
256
258
|
console.log(` \x1b[32m✓\x1b[0m Applied to ${found.relativePath}`);
|
|
257
|
-
results.push({ selector:
|
|
258
|
-
|
|
259
|
+
items.forEach(item => results.push({ selector: item.selector, ok: true, file: found.relativePath }));
|
|
259
260
|
} catch (aiErr) {
|
|
260
261
|
console.log(` \x1b[31m✗\x1b[0m AI error: ${aiErr.message}`);
|
|
261
|
-
results.push({ selector:
|
|
262
|
+
items.forEach(item => results.push({ selector: item.selector, ok: false, reason: aiErr.message }));
|
|
262
263
|
}
|
|
263
264
|
}
|
|
264
265
|
|