draply-dev 1.4.0 → 1.4.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 +18 -2
- package/package.json +1 -1
- package/src/overlay.js +159 -26
package/bin/cli.js
CHANGED
|
@@ -80,6 +80,19 @@ const server = http.createServer((req, res) => {
|
|
|
80
80
|
});
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
+
// ── Serve assets_draply ───────────────────────────────────────────────────
|
|
84
|
+
if (req.url.startsWith('/assets_draply/')) {
|
|
85
|
+
const filePath = path.join(process.cwd(), req.url.split('?')[0]);
|
|
86
|
+
if (fs.existsSync(filePath)) {
|
|
87
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
88
|
+
const mime = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.webp': 'image/webp' };
|
|
89
|
+
res.writeHead(200, { 'Content-Type': mime[ext] || 'application/octet-stream' });
|
|
90
|
+
fs.createReadStream(filePath).pipe(res);
|
|
91
|
+
} else {
|
|
92
|
+
res.writeHead(404); res.end();
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
83
96
|
|
|
84
97
|
// ── Config endpoint (get/set API key) ───────────────────────────────────────
|
|
85
98
|
const configPath = path.join(process.cwd(), 'draply.config.json');
|
|
@@ -204,7 +217,8 @@ const server = http.createServer((req, res) => {
|
|
|
204
217
|
|
|
205
218
|
items.forEach(item => {
|
|
206
219
|
const propsStr = Object.entries(item.props).map(([k, v]) => ` ${k}: ${v}`).join('\n');
|
|
207
|
-
changesBlock += `\nTarget Selector: ${item.selector}\nChanges:\n${propsStr}\n`;
|
|
220
|
+
changesBlock += `\nTarget Selector: ${item.selector}\nType: ${item.type}\nChanges:\n${propsStr}\n`;
|
|
221
|
+
if (item.type === 'create') changesBlock += `HTML to Insert: ${item.outerHTML}\n`;
|
|
208
222
|
});
|
|
209
223
|
|
|
210
224
|
const prompt = `You are a strict code editor applying style changes to a file.
|
|
@@ -223,8 +237,10 @@ Rules:
|
|
|
223
237
|
- The content inside <search> must be an EXACT substring from the file snippet above (including indentation).
|
|
224
238
|
- Update HTML/JSX inline styles or Tailwind classes appropriately.
|
|
225
239
|
- REPLACE old style values if they exist, DO NOT duplicate keys!
|
|
226
|
-
- If 'innerText' is provided in Changes, update the text content inside the target HTML element!
|
|
240
|
+
- If 'innerText' or 'innerHTML' is provided in Changes, update the text content inside the target HTML element! Use ONLY the exact text provided in the request (preserving internal HTML tags like spans for colors/styles if present). DO NOT add signatures, names, attributions, or "complete" the text based on context.
|
|
227
241
|
- If 'src' is provided in Changes, update the src attribute of the target HTML element!
|
|
242
|
+
- If a change is marked for a new element (type: create) and you cannot find the target selector, INSERT the provided 'outerHTML' before the closing </body> tag or in an appropriate container.
|
|
243
|
+
|
|
228
244
|
|
|
229
245
|
Example response:
|
|
230
246
|
<patch>
|
package/package.json
CHANGED
package/src/overlay.js
CHANGED
|
@@ -435,7 +435,7 @@
|
|
|
435
435
|
|
|
436
436
|
/* ── PLACED ASSETS ────────────────────────────────── */
|
|
437
437
|
.ps-asset-placed {
|
|
438
|
-
position:
|
|
438
|
+
position: absolute;
|
|
439
439
|
z-index: 999990;
|
|
440
440
|
cursor: grab;
|
|
441
441
|
user-select: none;
|
|
@@ -629,7 +629,8 @@
|
|
|
629
629
|
<input type="file" id="__ast_file__" accept="image/*" style="display:none">
|
|
630
630
|
<div id="__ast_list__" style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px"></div>
|
|
631
631
|
<div id="__ast_hint__" style="font-size:10px;color:#44446a;line-height:1.5;display:none">
|
|
632
|
-
Click
|
|
632
|
+
✦ Click to <b>place</b><br>
|
|
633
|
+
✦ Shift+Click to <b>replace</b> src/bg
|
|
633
634
|
</div>
|
|
634
635
|
<!-- Z-INDEX CONTROLS -->
|
|
635
636
|
<div id="__ast_zctrl__" style="display:none;margin-top:6px;">
|
|
@@ -1164,14 +1165,14 @@
|
|
|
1164
1165
|
// Make text directly editable
|
|
1165
1166
|
if (!el.isContentEditable) {
|
|
1166
1167
|
el.contentEditable = 'true';
|
|
1167
|
-
el.dataset.origText = el.
|
|
1168
|
+
el.dataset.origText = el.innerHTML || '';
|
|
1168
1169
|
el.focus();
|
|
1169
1170
|
|
|
1170
1171
|
const finishEdit = () => {
|
|
1171
1172
|
el.contentEditable = 'false';
|
|
1172
1173
|
el.removeEventListener('blur', finishEdit);
|
|
1173
|
-
if (el.
|
|
1174
|
-
rec(el, {
|
|
1174
|
+
if (el.innerHTML !== el.dataset.origText) {
|
|
1175
|
+
rec(el, { innerHTML: el.innerHTML }, { innerHTML: el.dataset.origText });
|
|
1175
1176
|
toast('Text updated!');
|
|
1176
1177
|
}
|
|
1177
1178
|
};
|
|
@@ -1335,19 +1336,27 @@
|
|
|
1335
1336
|
e.preventDefault(); e.stopPropagation();
|
|
1336
1337
|
if (!pendingAsset || !placingEl) return;
|
|
1337
1338
|
|
|
1338
|
-
if (e.
|
|
1339
|
-
|
|
1340
|
-
e.target.
|
|
1341
|
-
|
|
1342
|
-
|
|
1339
|
+
if (e.shiftKey) {
|
|
1340
|
+
// REPLACE mode
|
|
1341
|
+
if (e.target.tagName.toLowerCase() === 'img') {
|
|
1342
|
+
const prevSrc = e.target.getAttribute('src');
|
|
1343
|
+
e.target.src = pendingAsset.src;
|
|
1344
|
+
rec(e.target, { src: pendingAsset.src }, { src: prevSrc || '' });
|
|
1345
|
+
toast('🖼️ Image source replaced!');
|
|
1346
|
+
} else {
|
|
1347
|
+
const cs = getComputedStyle(e.target);
|
|
1348
|
+
const prevBg = cs.backgroundImage;
|
|
1349
|
+
e.target.style.backgroundImage = `url('${pendingAsset.src}')`;
|
|
1350
|
+
e.target.style.backgroundSize = 'cover';
|
|
1351
|
+
e.target.style.backgroundPosition = 'center';
|
|
1352
|
+
rec(e.target, { backgroundImage: `url('${pendingAsset.src}')`, backgroundSize: 'cover', backgroundPosition: 'center' }, { backgroundImage: prevBg });
|
|
1353
|
+
toast('🖼️ Background image set!');
|
|
1354
|
+
}
|
|
1343
1355
|
} else {
|
|
1344
|
-
|
|
1345
|
-
const
|
|
1346
|
-
e.
|
|
1347
|
-
|
|
1348
|
-
e.target.style.backgroundPosition = 'center';
|
|
1349
|
-
rec(e.target, { backgroundImage: `url('${pendingAsset.src}')`, backgroundSize: 'cover', backgroundPosition: 'center' }, { backgroundImage: prevBg });
|
|
1350
|
-
toast('🖼️ Background image set!');
|
|
1356
|
+
// PLACE mode
|
|
1357
|
+
const x = e.clientX - 60;
|
|
1358
|
+
const y = e.clientY - 60;
|
|
1359
|
+
placeAsset(pendingAsset, x, y, 120, 120);
|
|
1351
1360
|
}
|
|
1352
1361
|
cancelPlacing();
|
|
1353
1362
|
}
|
|
@@ -1365,13 +1374,127 @@
|
|
|
1365
1374
|
document.removeEventListener('keydown', onPlacingCancel);
|
|
1366
1375
|
}
|
|
1367
1376
|
|
|
1377
|
+
// Place asset permanently on page (absolute positioned)
|
|
1378
|
+
function placeAsset(asset, x, y, w, h) {
|
|
1379
|
+
const wrap = document.createElement('div');
|
|
1380
|
+
wrap.className = 'ps-asset-placed';
|
|
1381
|
+
const uid = 'ps-asset-' + Date.now();
|
|
1382
|
+
wrap.id = uid;
|
|
1383
|
+
wrap.style.cssText = `left:${Math.round(x)}px;top:${Math.round(y)}px;width:${w}px;height:${h}px;z-index:1;`;
|
|
1384
|
+
wrap.innerHTML = `<img src="${asset.src}" draggable="false" alt="${asset.name}">`;
|
|
1385
|
+
document.body.appendChild(wrap);
|
|
1386
|
+
|
|
1387
|
+
// Click to select this placed asset (for z-index control)
|
|
1388
|
+
wrap.addEventListener('click', e => {
|
|
1389
|
+
if (ps(e.target)) return;
|
|
1390
|
+
selectPlaced(wrap);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// Make placed asset draggable
|
|
1394
|
+
wrap.addEventListener('mousedown', e => {
|
|
1395
|
+
if (ps(e.target)) return;
|
|
1396
|
+
e.preventDefault();
|
|
1397
|
+
selectPlaced(wrap);
|
|
1398
|
+
astDragEl = wrap;
|
|
1399
|
+
astDragSX = e.clientX; astDragSY = e.clientY;
|
|
1400
|
+
astDragOL = parseFloat(wrap.style.left) || 0;
|
|
1401
|
+
astDragOT = parseFloat(wrap.style.top) || 0;
|
|
1402
|
+
wrap.style.cursor = 'grabbing';
|
|
1403
|
+
state.dragging = true;
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
rec(wrap, {
|
|
1407
|
+
src: asset.src,
|
|
1408
|
+
left: Math.round(x) + 'px',
|
|
1409
|
+
top: Math.round(y) + 'px',
|
|
1410
|
+
width: w + 'px',
|
|
1411
|
+
height: h + 'px',
|
|
1412
|
+
'z-index': '1',
|
|
1413
|
+
}, null, true); // true = isCreate
|
|
1414
|
+
|
|
1415
|
+
toast('✦ Placed — drag to reposition');
|
|
1416
|
+
selectPlaced(wrap);
|
|
1417
|
+
return wrap;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// Track selected placed asset for z-index controls
|
|
1421
|
+
let selectedPlaced = null;
|
|
1422
|
+
const zCtrl = document.getElementById('__ast_zctrl__');
|
|
1423
|
+
const zVal = document.getElementById('__z_val__');
|
|
1424
|
+
const zFront = document.getElementById('__z_front__');
|
|
1425
|
+
const zBack = document.getElementById('__z_back__');
|
|
1426
|
+
const zSet = document.getElementById('__z_set__');
|
|
1427
|
+
|
|
1428
|
+
function selectPlaced(wrap) {
|
|
1429
|
+
// Deselect previous
|
|
1430
|
+
if (selectedPlaced) selectedPlaced.style.outline = '';
|
|
1431
|
+
selectedPlaced = wrap;
|
|
1432
|
+
wrap.style.outline = '2px solid #7fff6e';
|
|
1433
|
+
zVal.value = parseInt(wrap.style.zIndex) || 1;
|
|
1434
|
+
zCtrl.style.display = 'block';
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
zFront.onclick = () => {
|
|
1438
|
+
if (!selectedPlaced) return;
|
|
1439
|
+
// Find max z-index of all placed assets
|
|
1440
|
+
const max = Math.max(0, ...[...document.querySelectorAll('.ps-asset-placed')].map(el => parseInt(el.style.zIndex) || 0));
|
|
1441
|
+
const nz = max + 1;
|
|
1442
|
+
selectedPlaced.style.zIndex = nz;
|
|
1443
|
+
zVal.value = nz;
|
|
1444
|
+
rec(selectedPlaced, { 'z-index': String(nz) });
|
|
1445
|
+
toast('▲ Moved to front (z:' + nz + ')');
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
zBack.onclick = () => {
|
|
1449
|
+
if (!selectedPlaced) return;
|
|
1450
|
+
const min = Math.min(0, ...[...document.querySelectorAll('.ps-asset-placed')].map(el => parseInt(el.style.zIndex) || 0));
|
|
1451
|
+
const nz = min - 1;
|
|
1452
|
+
selectedPlaced.style.zIndex = nz;
|
|
1453
|
+
zVal.value = nz;
|
|
1454
|
+
rec(selectedPlaced, { 'z-index': String(nz) });
|
|
1455
|
+
toast('▼ Moved to back (z:' + nz + ')');
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
zSet.onclick = () => {
|
|
1459
|
+
if (!selectedPlaced) return;
|
|
1460
|
+
const nz = parseInt(zVal.value) || 0;
|
|
1461
|
+
selectedPlaced.style.zIndex = nz;
|
|
1462
|
+
rec(selectedPlaced, { 'z-index': String(nz) });
|
|
1463
|
+
toast('z-index set to ' + nz);
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
zVal.onkeydown = e => { if (e.key === 'Enter') zSet.click(); };
|
|
1467
|
+
|
|
1468
|
+
// Drag placed assets
|
|
1469
|
+
document.addEventListener('mousemove', e => {
|
|
1470
|
+
if (!astDragEl || !state.dragging) return;
|
|
1471
|
+
const dx = e.clientX - astDragSX, dy = e.clientY - astDragSY;
|
|
1472
|
+
astDragEl.style.left = (astDragOL + dx) + 'px';
|
|
1473
|
+
astDragEl.style.top = (astDragOT + dy) + 'px';
|
|
1474
|
+
tip.textContent = `x:${Math.round(astDragOL + dx)} y:${Math.round(astDragOT + dy)}`;
|
|
1475
|
+
tip.style.left = (e.clientX + 14) + 'px'; tip.style.top = (e.clientY - 28) + 'px';
|
|
1476
|
+
tip.classList.add('v');
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
document.addEventListener('mouseup', () => {
|
|
1480
|
+
if (!astDragEl) return;
|
|
1481
|
+
tip.classList.remove('v');
|
|
1482
|
+
const cs = getComputedStyle(astDragEl);
|
|
1483
|
+
rec(astDragEl, {
|
|
1484
|
+
left: Math.round(parseFloat(cs.left)) + 'px',
|
|
1485
|
+
top: Math.round(parseFloat(cs.top)) + 'px',
|
|
1486
|
+
});
|
|
1487
|
+
astDragEl.style.cursor = 'grab';
|
|
1488
|
+
astDragEl = null;
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1368
1491
|
// ══════════════════════════════════════════
|
|
1369
1492
|
// RECORD + SAVE
|
|
1370
1493
|
// ══════════════════════════════════════════
|
|
1371
1494
|
// Full history — every individual action
|
|
1372
1495
|
const history = [];
|
|
1373
1496
|
|
|
1374
|
-
function rec(el, props, prevPropsOverride) {
|
|
1497
|
+
function rec(el, props, prevPropsOverride, isCreate = false) {
|
|
1375
1498
|
const selector = el.dataset.pixelshiftId ? null : gsel(el);
|
|
1376
1499
|
const key = el.dataset.pixelshiftId || selector;
|
|
1377
1500
|
|
|
@@ -1401,19 +1524,22 @@
|
|
|
1401
1524
|
|
|
1402
1525
|
// Merge into state.changes (for save/apply)
|
|
1403
1526
|
const ch = {
|
|
1404
|
-
type: el.dataset.pixelshiftId ? 'inline' : 'css',
|
|
1527
|
+
type: isCreate ? 'create' : (el.dataset.pixelshiftId ? 'inline' : 'css'),
|
|
1528
|
+
isCreate,
|
|
1405
1529
|
pixelshiftId: el.dataset.pixelshiftId || null,
|
|
1406
1530
|
selector,
|
|
1407
1531
|
exactFile: getReactSource(el),
|
|
1408
1532
|
file: el.dataset.pixelshiftFile || null,
|
|
1409
|
-
props
|
|
1533
|
+
props,
|
|
1534
|
+
tagName: el.tagName.toLowerCase(),
|
|
1535
|
+
outerHTML: el.outerHTML // Send the whole element for 'create' actions
|
|
1410
1536
|
};
|
|
1411
1537
|
const i = state.changes.findIndex(c => (c.pixelshiftId || c.selector) === key);
|
|
1412
1538
|
if (i >= 0) Object.assign(state.changes[i].props, props); else state.changes.push(ch);
|
|
1413
1539
|
|
|
1414
1540
|
// Push to history
|
|
1415
1541
|
const hid = Date.now() + Math.random();
|
|
1416
|
-
history.push({ hid, el, props, prevProps, selector: key });
|
|
1542
|
+
history.push({ hid, el, props, prevProps, selector: key, isCreate });
|
|
1417
1543
|
|
|
1418
1544
|
updateUnsUI();
|
|
1419
1545
|
}
|
|
@@ -1442,11 +1568,18 @@
|
|
|
1442
1568
|
}
|
|
1443
1569
|
|
|
1444
1570
|
function revertChange(h) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1571
|
+
if (h.isCreate) {
|
|
1572
|
+
h.el.remove();
|
|
1573
|
+
} else {
|
|
1574
|
+
// Re-apply previous inline values
|
|
1575
|
+
Object.entries(h.prevProps).forEach(([prop, val]) => {
|
|
1576
|
+
const camel = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1577
|
+
h.el.style[camel] = val;
|
|
1578
|
+
});
|
|
1579
|
+
// Also handle text/html
|
|
1580
|
+
if (h.prevProps.innerHTML !== undefined) h.el.innerHTML = h.prevProps.innerHTML;
|
|
1581
|
+
if (h.prevProps.innerText !== undefined) h.el.innerText = h.prevProps.innerText;
|
|
1582
|
+
}
|
|
1450
1583
|
// Remove from history
|
|
1451
1584
|
const idx = history.findIndex(x => x.hid === h.hid);
|
|
1452
1585
|
if (idx >= 0) history.splice(idx, 1);
|