draply-dev 1.3.4 → 1.3.5

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 (2) hide show
  1. package/bin/cli.js +73 -24
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -262,47 +262,96 @@ const server = http.createServer((req, res) => {
262
262
  for (const [filePath, { found, items }] of fileChanges) {
263
263
  console.log(` \x1b[36m🤖\x1b[0m AI applying ${items.length} changes → ${found.relativePath}`);
264
264
 
265
- // Build combined prompt
265
+ // Find the relevant context snippet (~50 lines around the class usage)
266
+ const lines = found.content.split('\n');
267
+ let contextStart = 0, contextEnd = lines.length - 1;
268
+ for (const item of items) {
269
+ for (let i = 0; i < lines.length; i++) {
270
+ if (lines[i].includes(item.className)) {
271
+ contextStart = Math.min(contextStart || i, Math.max(0, i - 25));
272
+ contextEnd = Math.min(lines.length - 1, i + 25);
273
+ }
274
+ }
275
+ }
276
+ const snippet = lines.slice(contextStart, contextEnd + 1).join('\n');
277
+
278
+ // Build changes description
266
279
  let changesBlock = '';
267
280
  items.forEach(item => {
268
281
  const propsStr = Object.entries(item.props).map(([k, v]) => ` ${k}: ${v}`).join('\n');
269
282
  changesBlock += `\nElement .${item.className}:\n${propsStr}\n`;
270
283
  });
271
284
 
272
- const prompt = `You are a code editor. Modify the file to apply CSS style changes.
285
+ const prompt = `You are a code editor. I need to apply CSS style changes to a file.
273
286
 
274
- FILE: ${found.relativePath}
287
+ FILE: ${found.relativePath} (lines ${contextStart + 1}-${contextEnd + 1})
275
288
  \`\`\`
276
- ${found.content}
289
+ ${snippet}
277
290
  \`\`\`
278
291
 
279
- CHANGES TO APPLY:
292
+ CHANGES:
280
293
  ${changesBlock}
281
- RULES:
282
- - If CSS/SCSS file: find each class rule and update/add properties. Add rules if missing.
283
- - If JSX/TSX file: apply in the most appropriate way:
284
- - Existing inline styles → update them
285
- - File imports CSS → note which CSS file to edit
286
- - Tailwind classes → update className with Tailwind classes
287
- - Otherwise → add style prop
288
- - Keep ALL existing code intact. Only modify what's needed.
289
- - Do NOT add extra comments.
290
-
291
- Return ONLY the complete modified file. No markdown fences, no explanations.`;
294
+ IMPORTANT: Return ONLY a JSON array of search-and-replace pairs. Each pair is:
295
+ {"search": "exact existing code to find", "replace": "modified code to replace it with"}
296
+
297
+ Rules:
298
+ - "search" must be an EXACT substring from the file above (copy it precisely)
299
+ - "search" should be minimal — just the line(s) that need changing
300
+ - For CSS: modify the class rule properties. If rule doesn't exist, set search to the closing bracket of the nearest rule and add the new rule after it.
301
+ - For JSX with className: add/update the style prop on that element
302
+ - For JSX with imported CSS: add CSS rules (search for the import line, replace with import + note)
303
+ - Keep changes minimal
304
+
305
+ Example response:
306
+ [{"search": "className=\\"hero\\"", "replace": "className=\\"hero\\" style={{color: 'red', fontSize: '20px'}}"}]
307
+
308
+ Return ONLY valid JSON array. No markdown, no explanation.`;
292
309
 
293
310
  try {
294
311
  const result = await callAI(cfg.apiKey, prompt, cfg.provider || 'groq');
295
- let code = result.trim();
296
- if (code.startsWith('```')) {
297
- code = code.replace(/^```[a-z]*\n?/, '').replace(/\n?```$/, '');
312
+ let jsonStr = result.trim();
313
+ // Clean markdown fences if present
314
+ if (jsonStr.startsWith('```')) {
315
+ jsonStr = jsonStr.replace(/^```[a-z]*\n?/, '').replace(/\n?```$/, '');
298
316
  }
299
- if (code.length < 20) {
300
- items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI returned invalid response' }));
317
+
318
+ let patches;
319
+ try {
320
+ patches = JSON.parse(jsonStr);
321
+ } catch {
322
+ console.log(` \x1b[31m✗\x1b[0m AI returned invalid JSON`);
323
+ console.log(` Response: ${jsonStr.substring(0, 200)}`);
324
+ items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI returned invalid JSON' }));
301
325
  continue;
302
326
  }
303
- fs.writeFileSync(found.file, code, 'utf8');
304
- console.log(` \x1b[32m✓\x1b[0m Applied to ${found.relativePath}`);
305
- items.forEach(item => results.push({ selector: item.selector, ok: true, file: found.relativePath }));
327
+
328
+ if (!Array.isArray(patches) || patches.length === 0) {
329
+ items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI returned no changes' }));
330
+ continue;
331
+ }
332
+
333
+ // Apply patches
334
+ let content = found.content;
335
+ let applied = 0;
336
+ for (const patch of patches) {
337
+ if (!patch.search || patch.replace === undefined) continue;
338
+ if (content.includes(patch.search)) {
339
+ content = content.replace(patch.search, patch.replace);
340
+ applied++;
341
+ console.log(` \x1b[32m ✓\x1b[0m Patch applied`);
342
+ } else {
343
+ console.log(` \x1b[33m âš \x1b[0m Search string not found: "${patch.search.substring(0, 60)}..."`);
344
+ }
345
+ }
346
+
347
+ if (applied > 0) {
348
+ fs.writeFileSync(found.file, content, 'utf8');
349
+ console.log(` \x1b[32m✓\x1b[0m Applied ${applied} patches to ${found.relativePath}`);
350
+ items.forEach(item => results.push({ selector: item.selector, ok: true, file: found.relativePath }));
351
+ } else {
352
+ items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'No patches matched' }));
353
+ }
354
+
306
355
  } catch (aiErr) {
307
356
  console.log(` \x1b[31m✗\x1b[0m AI error: ${aiErr.message}`);
308
357
  items.forEach(item => results.push({ selector: item.selector, ok: false, reason: aiErr.message }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "draply-dev",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "Visual overlay for any frontend project — move, resize, restyle live in the browser, save to CSS",
5
5
  "author": "Arman",
6
6
  "type": "commonjs",