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.
- package/bin/cli.js +34 -16
- 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 =
|
|
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
|
-
|
|
142
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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(
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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(
|
|
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); });
|