draply-dev 1.0.7 → 1.0.8

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 +34 -16
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ const targetPort = parseInt(args[0]) || 3000;
11
11
  const proxyPort = parseInt(args[1]) || 4000;
12
12
  const targetHost = args[2] || 'localhost';
13
13
 
14
+ const pkg = require('../package.json');
14
15
  const OVERLAY_JS = fs.readFileSync(path.join(__dirname, '../src/overlay.js'), 'utf8');
15
16
  // Unique marker that does NOT appear inside overlay.js itself
16
17
  const MARKER = 'data-ps-done="1"';
@@ -126,22 +127,27 @@ const server = http.createServer((req, res) => {
126
127
  }
127
128
 
128
129
  // 2. Call AI per file using XML Patches
129
- for (const [filePath, { content, items }] of fileMap) {
130
+ for (const [filePath, { content: rawContent, items }] of fileMap) {
130
131
 
131
132
  let changesBlock = '';
133
+ const content = rawContent.replace(/\r\n/g, '\n');
132
134
  const lines = content.split('\n');
133
135
  let classNamesToFind = items.map(c => {
134
136
  const parts = c.selector.split('.');
135
137
  return parts.length > 1 ? parts.pop() : '';
136
138
  }).filter(Boolean);
137
139
 
138
- let ctxStart = 0, ctxEnd = lines.length - 1;
140
+ let ctxStart = -1, ctxEnd = -1;
139
141
  for (let i = 0; i < lines.length; i++) {
140
- if (classNamesToFind.some(cls => lines[i].includes(cls))) {
141
- ctxStart = Math.max(0, i - 25);
142
- ctxEnd = Math.min(lines.length - 1, i + 25);
142
+ if (classNamesToFind.some(cls => lines[i].includes(cls)) || (items.length === 1 && items[0].selector && lines[i].includes(items[0].selector.split('#').pop()))) {
143
+ const s = Math.max(0, i - 40);
144
+ const e = Math.min(lines.length - 1, i + 40);
145
+ if (ctxStart === -1 || s < ctxStart) ctxStart = s;
146
+ if (ctxEnd === -1 || e > ctxEnd) ctxEnd = e;
143
147
  }
144
148
  }
149
+ if (ctxStart === -1) { ctxStart = 0; ctxEnd = Math.min(lines.length - 1, 150); }
150
+
145
151
  const snippet = lines.slice(ctxStart, ctxEnd + 1).join('\n');
146
152
 
147
153
  items.forEach(item => {
@@ -162,7 +168,7 @@ ${changesBlock}
162
168
  IMPORTANT: Return your changes wrapped in <patch> tags containing <search> and <replace> blocks. DO NOT use JSON.
163
169
 
164
170
  Rules:
165
- - The content inside <search> must be an EXACT substring from the file snippet above.
171
+ - The content inside <search> must be an EXACT substring from the file snippet above (including indentation).
166
172
  - Update HTML/JSX inline styles or Tailwind classes appropriately.
167
173
  - REPLACE old style values if they exist, DO NOT duplicate keys!
168
174
 
@@ -209,10 +215,8 @@ Return ONLY the patch blocks.`;
209
215
  let match;
210
216
  while ((match = patchRegex.exec(apiResult)) !== null) {
211
217
  let s = match[1], r = match[2];
212
- if (s.startsWith('\n')) s = s.substring(1);
213
- if (s.endsWith('\n')) s = s.substring(0, s.length - 1);
214
- if (r.startsWith('\n')) r = r.substring(1);
215
- if (r.endsWith('\n')) r = r.substring(0, r.length - 1);
218
+ s = s.replace(/^\n+/, '').replace(/\n+$/, '').replace(/\r/g, '');
219
+ r = r.replace(/^\n+/, '').replace(/\n+$/, '').replace(/\r/g, '');
216
220
  if (s.trim()) patches.push({ search: s, replace: r });
217
221
  }
218
222
 
@@ -225,7 +229,7 @@ Return ONLY the patch blocks.`;
225
229
  } else {
226
230
  // Fallback to whitespace-agnostic matching
227
231
  const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
228
- const looseRegexStr = escapeRegExp(patch.search.trim()).replace(/\\s+/g, '\\s+');
232
+ const looseRegexStr = escapeRegExp(patch.search.trim()).replace(/\s+/g, '\\s+');
229
233
  const looseRegex = new RegExp(looseRegexStr);
230
234
  if (looseRegex.test(newContent)) {
231
235
  newContent = newContent.replace(looseRegex, patch.replace);
@@ -235,13 +239,17 @@ Return ONLY the patch blocks.`;
235
239
  }
236
240
 
237
241
  if (applied > 0) {
238
- fs.writeFileSync(filePath, newContent, 'utf8');
239
- console.log(` \x1b[32m✓\x1b[0m Modified ${path.basename(filePath)}`);
240
- items.forEach(item => results.push({ selector: item.selector, ok: true }));
242
+ // Restore original line endings if they were \r\n
243
+ const finalContent = rawContent.includes('\r\n') ? newContent.replace(/\n/g, '\r\n') : newContent;
244
+ fs.writeFileSync(filePath, finalContent, 'utf8');
245
+ console.log(` \x1b[32m✓\x1b[0m Modified ${path.basename(filePath)} (${applied} patch(es))`);
246
+ items.forEach(item => results.push({ selector: item.selector, ok: true }));
241
247
  } else {
242
- items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI patch failed to match' }));
248
+ console.warn(` \x1b[33m⚠\x1b[0m AI matched 0 patches in ${path.basename(filePath)}`);
249
+ items.forEach(item => results.push({ selector: item.selector, ok: false, reason: 'AI patch failed to match' }));
243
250
  }
244
251
  } catch (e) {
252
+ console.error(` \x1b[31m✖\x1b[0m AI Error for ${path.basename(filePath)}:`, e.message);
245
253
  items.forEach(item => results.push({ selector: item.selector, ok: false, reason: e.message }));
246
254
  }
247
255
  }
@@ -331,10 +339,20 @@ Return ONLY the patch blocks.`;
331
339
  });
332
340
 
333
341
  server.listen(proxyPort, () => {
334
- console.log('\n \x1b[32m●\x1b[0m Draply запущен\n');
342
+ console.log(`\n \x1b[32m●\x1b[0m Draply v${pkg.version} запущен\n`);
335
343
  console.log(` Твой проект → \x1b[36mhttp://${targetHost}:${targetPort}\x1b[0m`);
336
344
  console.log(` Открой это → \x1b[33mhttp://localhost:${proxyPort}\x1b[0m \x1b[32m← вот сюда заходи!\x1b[0m\n`);
337
345
  console.log(` \x1b[90mCtrl+C чтобы остановить\x1b[0m\n`);
338
346
  });
339
347
 
348
+ server.on('error', (e) => {
349
+ if (e.code === 'EADDRINUSE') {
350
+ console.error(`\x1b[31m✖ Ошибка: Порт ${proxyPort} уже занят.\x1b[0m`);
351
+ console.log(`Попробуй другой порт: \x1b[33mnpx draply ${targetPort} ${proxyPort + 1}\x1b[0m`);
352
+ } else {
353
+ console.error(`\x1b[31m✖ Ошибка сервера:\x1b[0m`, e.message);
354
+ }
355
+ process.exit(1);
356
+ });
357
+
340
358
  process.on('SIGINT', () => { console.log('\n \x1b[90mDraply остановлен\x1b[0m\n'); process.exit(0); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "draply-dev",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
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",