uivisor 0.1.2 → 0.1.4
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/dist/next/index.cjs +5 -4
- package/dist/next/index.js +5 -4
- package/dist/overlay/index.js +265 -26
- package/package.json +1 -1
package/dist/next/index.cjs
CHANGED
|
@@ -96,12 +96,13 @@ function withUivisor(nextConfig = {}, options = {}) {
|
|
|
96
96
|
}
|
|
97
97
|
if (useTurbopack) {
|
|
98
98
|
const ld = [{ loader: "uivisor/next/loader", options: { attr } }];
|
|
99
|
+
const major = nextMajor();
|
|
100
|
+
const modern = major === 0 || major >= 15;
|
|
99
101
|
const rules = {
|
|
100
|
-
"*.tsx": { loaders: ld, as: "*.tsx" },
|
|
101
|
-
"*.jsx": { loaders: ld, as: "*.jsx" }
|
|
102
|
+
"*.tsx": modern ? { loaders: ld } : { loaders: ld, as: "*.tsx" },
|
|
103
|
+
"*.jsx": modern ? { loaders: ld } : { loaders: ld, as: "*.jsx" }
|
|
102
104
|
};
|
|
103
|
-
|
|
104
|
-
if (major === 0 || major >= 15) {
|
|
105
|
+
if (modern) {
|
|
105
106
|
out.turbopack = {
|
|
106
107
|
...nextConfig.turbopack,
|
|
107
108
|
rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
|
package/dist/next/index.js
CHANGED
|
@@ -68,12 +68,13 @@ function withUivisor(nextConfig = {}, options = {}) {
|
|
|
68
68
|
}
|
|
69
69
|
if (useTurbopack) {
|
|
70
70
|
const ld = [{ loader: "uivisor/next/loader", options: { attr } }];
|
|
71
|
+
const major = nextMajor();
|
|
72
|
+
const modern = major === 0 || major >= 15;
|
|
71
73
|
const rules = {
|
|
72
|
-
"*.tsx": { loaders: ld, as: "*.tsx" },
|
|
73
|
-
"*.jsx": { loaders: ld, as: "*.jsx" }
|
|
74
|
+
"*.tsx": modern ? { loaders: ld } : { loaders: ld, as: "*.tsx" },
|
|
75
|
+
"*.jsx": modern ? { loaders: ld } : { loaders: ld, as: "*.jsx" }
|
|
74
76
|
};
|
|
75
|
-
|
|
76
|
-
if (major === 0 || major >= 15) {
|
|
77
|
+
if (modern) {
|
|
77
78
|
out.turbopack = {
|
|
78
79
|
...nextConfig.turbopack,
|
|
79
80
|
rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
|
package/dist/overlay/index.js
CHANGED
|
@@ -253,6 +253,62 @@ function suggestUtility(property, value) {
|
|
|
253
253
|
if (!hex) return null;
|
|
254
254
|
return property === "color" ? `text-[${hex}]` : `bg-[${hex}]`;
|
|
255
255
|
}
|
|
256
|
+
const LAYOUT = {
|
|
257
|
+
display: {
|
|
258
|
+
block: "block",
|
|
259
|
+
"inline-block": "inline-block",
|
|
260
|
+
inline: "inline",
|
|
261
|
+
flex: "flex",
|
|
262
|
+
"inline-flex": "inline-flex",
|
|
263
|
+
grid: "grid",
|
|
264
|
+
"inline-grid": "inline-grid",
|
|
265
|
+
none: "hidden"
|
|
266
|
+
},
|
|
267
|
+
"flex-direction": {
|
|
268
|
+
row: "flex-row",
|
|
269
|
+
"row-reverse": "flex-row-reverse",
|
|
270
|
+
column: "flex-col",
|
|
271
|
+
"column-reverse": "flex-col-reverse"
|
|
272
|
+
},
|
|
273
|
+
"flex-wrap": { nowrap: "flex-nowrap", wrap: "flex-wrap", "wrap-reverse": "flex-wrap-reverse" },
|
|
274
|
+
"justify-content": {
|
|
275
|
+
normal: "justify-normal",
|
|
276
|
+
"flex-start": "justify-start",
|
|
277
|
+
center: "justify-center",
|
|
278
|
+
"flex-end": "justify-end",
|
|
279
|
+
"space-between": "justify-between",
|
|
280
|
+
"space-around": "justify-around",
|
|
281
|
+
"space-evenly": "justify-evenly"
|
|
282
|
+
},
|
|
283
|
+
"align-items": {
|
|
284
|
+
normal: "items-normal",
|
|
285
|
+
stretch: "items-stretch",
|
|
286
|
+
"flex-start": "items-start",
|
|
287
|
+
center: "items-center",
|
|
288
|
+
"flex-end": "items-end",
|
|
289
|
+
baseline: "items-baseline"
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
if (LAYOUT[property]) return LAYOUT[property][value.trim()] ?? null;
|
|
293
|
+
const SIZE_PREFIX = {
|
|
294
|
+
width: "w",
|
|
295
|
+
height: "h",
|
|
296
|
+
"min-width": "min-w",
|
|
297
|
+
"max-width": "max-w",
|
|
298
|
+
"min-height": "min-h",
|
|
299
|
+
"max-height": "max-h"
|
|
300
|
+
};
|
|
301
|
+
if (SIZE_PREFIX[property]) {
|
|
302
|
+
const pre = SIZE_PREFIX[property];
|
|
303
|
+
const t = value.trim();
|
|
304
|
+
if (t === "auto") return `${pre}-auto`;
|
|
305
|
+
if (t === "100%") return `${pre}-full`;
|
|
306
|
+
const n = px(t);
|
|
307
|
+
if (n == null) return `${pre}-[${t}]`;
|
|
308
|
+
const scale = n / 4;
|
|
309
|
+
if (Number.isInteger(scale) && scale >= 0 && scale <= 96) return `${pre}-${scale}`;
|
|
310
|
+
return `${pre}-[${n}px]`;
|
|
311
|
+
}
|
|
256
312
|
return null;
|
|
257
313
|
}
|
|
258
314
|
|
|
@@ -306,9 +362,61 @@ var ICONS = {
|
|
|
306
362
|
line: sv('<path d="M2.5 3 H13.5 M2.5 8 H13.5 M2.5 13 H13.5"/>'),
|
|
307
363
|
tracking: sv('<path d="M5 4 V12 M11 4 V12"/><path d="M2.5 8 H4 M1.8 6.6 L0.8 8 L1.8 9.4"/><path d="M13.5 8 H12 M14.2 6.6 L15.2 8 L14.2 9.4"/>'),
|
|
308
364
|
expand: sv('<rect x="2" y="2" width="4.5" height="4.5" rx="1"/><rect x="9.5" y="2" width="4.5" height="4.5" rx="1"/><rect x="2" y="9.5" width="4.5" height="4.5" rx="1"/><rect x="9.5" y="9.5" width="4.5" height="4.5" rx="1"/>'),
|
|
309
|
-
collapse: sv('<rect x="3" y="3" width="10" height="10" rx="2"/>')
|
|
365
|
+
collapse: sv('<rect x="3" y="3" width="10" height="10" rx="2"/>'),
|
|
366
|
+
layout: sv('<rect x="2" y="2" width="12" height="12" rx="1.5"/><path d="M6 2.5 V13.5 M6 6 H13.5"/>'),
|
|
367
|
+
width: sv('<path d="M1.5 8 H14.5 M4 5 L1.5 8 L4 11 M12 5 L14.5 8 L12 11"/>'),
|
|
368
|
+
height: sv('<path d="M8 1.5 V14.5 M5 4 L8 1.5 L11 4 M5 12 L8 14.5 L11 12"/>'),
|
|
369
|
+
chevron: sv('<path d="M6 4 L10 8 L6 12"/>')
|
|
310
370
|
};
|
|
311
371
|
var SECTIONS = [
|
|
372
|
+
{
|
|
373
|
+
title: "Layout",
|
|
374
|
+
controls: [
|
|
375
|
+
{
|
|
376
|
+
kind: "select",
|
|
377
|
+
css: "display",
|
|
378
|
+
label: "Display",
|
|
379
|
+
options: ["block", "inline-block", "inline", "flex", "inline-flex", "grid", "inline-grid", "none"]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
kind: "select",
|
|
383
|
+
css: "flex-direction",
|
|
384
|
+
label: "Direction",
|
|
385
|
+
options: ["row", "row-reverse", "column", "column-reverse"],
|
|
386
|
+
requires: "flexgrid"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
kind: "select",
|
|
390
|
+
css: "justify-content",
|
|
391
|
+
label: "Justify",
|
|
392
|
+
options: ["normal", "flex-start", "center", "flex-end", "space-between", "space-around", "space-evenly"],
|
|
393
|
+
requires: "flexgrid"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
kind: "select",
|
|
397
|
+
css: "align-items",
|
|
398
|
+
label: "Align",
|
|
399
|
+
options: ["normal", "stretch", "flex-start", "center", "flex-end", "baseline"],
|
|
400
|
+
requires: "flexgrid"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
kind: "select",
|
|
404
|
+
css: "flex-wrap",
|
|
405
|
+
label: "Wrap",
|
|
406
|
+
options: ["nowrap", "wrap", "wrap-reverse"],
|
|
407
|
+
requires: "flexgrid"
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
title: "Size",
|
|
413
|
+
controls: [
|
|
414
|
+
{ kind: "len", css: "width", label: "Width", icon: ICONS.width },
|
|
415
|
+
{ kind: "len", css: "height", label: "Height", icon: ICONS.height },
|
|
416
|
+
{ kind: "len", css: "max-width", label: "Max W", icon: ICONS.width },
|
|
417
|
+
{ kind: "len", css: "min-height", label: "Min H", icon: ICONS.height }
|
|
418
|
+
]
|
|
419
|
+
},
|
|
312
420
|
{
|
|
313
421
|
title: "Spacing",
|
|
314
422
|
controls: [
|
|
@@ -909,6 +1017,17 @@ var CSS = (
|
|
|
909
1017
|
letter-spacing: .5px; color: #8b8b94; font-weight: 600; }
|
|
910
1018
|
.uiv-sec + .uiv-sec .uiv-sectitle { margin-top: 0; }
|
|
911
1019
|
|
|
1020
|
+
/* collapsible (accordion) section header */
|
|
1021
|
+
.uiv-acc { display: flex; align-items: center; gap: 5px; width: 100%;
|
|
1022
|
+
background: none; border: 0; padding: 0; cursor: pointer; text-align: left;
|
|
1023
|
+
font-size: 10px; text-transform: uppercase; letter-spacing: .5px;
|
|
1024
|
+
color: #8b8b94; font-weight: 600; }
|
|
1025
|
+
.uiv-acc:hover { color: #c7d2fe; }
|
|
1026
|
+
.uiv-acc.collapsed { margin-bottom: 0; }
|
|
1027
|
+
.uiv-chev { display: inline-flex; color: #6b6b73; transition: transform .15s ease; }
|
|
1028
|
+
.uiv-acc:hover .uiv-chev { color: #818cf8; }
|
|
1029
|
+
.uiv-acc:not(.collapsed) .uiv-chev { transform: rotate(90deg); }
|
|
1030
|
+
|
|
912
1031
|
.uiv-ctl { display: grid; grid-template-columns: 70px 1fr 26px; gap: 8px;
|
|
913
1032
|
align-items: center; margin-bottom: 7px; }
|
|
914
1033
|
.uiv-ctl:last-child { margin-bottom: 0; }
|
|
@@ -997,6 +1116,14 @@ var Uivisor = class {
|
|
|
997
1116
|
this.selected = null;
|
|
998
1117
|
this.states = /* @__PURE__ */ new Map();
|
|
999
1118
|
this.expanded = /* @__PURE__ */ new Set();
|
|
1119
|
+
/** Section titles collapsed in the accordion (per session). */
|
|
1120
|
+
this.collapsedSecs = /* @__PURE__ */ new Set();
|
|
1121
|
+
/** Undo / redo stacks of full edit-state snapshots. */
|
|
1122
|
+
this.undoStack = [];
|
|
1123
|
+
this.redoStack = [];
|
|
1124
|
+
/** Cached live computed-style for the selected element (invalidated on reselect). */
|
|
1125
|
+
this._cs = null;
|
|
1126
|
+
this._csEl = null;
|
|
1000
1127
|
/** Cached project breakpoint system (detected from CSS), refreshed until found. */
|
|
1001
1128
|
this._bp = null;
|
|
1002
1129
|
// responsive (virtual screen) mode
|
|
@@ -1032,7 +1159,20 @@ var Uivisor = class {
|
|
|
1032
1159
|
if (e.altKey && (e.key === "u" || e.key === "U")) {
|
|
1033
1160
|
e.preventDefault();
|
|
1034
1161
|
this.toggle();
|
|
1035
|
-
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (!this.enabled) return;
|
|
1165
|
+
if ((e.metaKey || e.ctrlKey) && (e.key === "z" || e.key === "Z")) {
|
|
1166
|
+
e.preventDefault();
|
|
1167
|
+
e.shiftKey ? this.redo() : this.undo();
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if ((e.metaKey || e.ctrlKey) && (e.key === "y" || e.key === "Y")) {
|
|
1171
|
+
e.preventDefault();
|
|
1172
|
+
this.redo();
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (e.key === "Escape") {
|
|
1036
1176
|
if (this.selected) this.select(null);
|
|
1037
1177
|
else this.toggle(false);
|
|
1038
1178
|
}
|
|
@@ -1270,7 +1410,6 @@ var Uivisor = class {
|
|
|
1270
1410
|
const up = () => {
|
|
1271
1411
|
handle.removeEventListener("pointermove", move);
|
|
1272
1412
|
handle.removeEventListener("pointerup", up);
|
|
1273
|
-
this.retagSelected();
|
|
1274
1413
|
this.renderBody();
|
|
1275
1414
|
};
|
|
1276
1415
|
handle.addEventListener("pointermove", move);
|
|
@@ -1313,11 +1452,26 @@ var Uivisor = class {
|
|
|
1313
1452
|
st() {
|
|
1314
1453
|
return this.selected ? this.states.get(this.selected) ?? null : null;
|
|
1315
1454
|
}
|
|
1455
|
+
/** Live computed value of the selected element — reflects the CURRENT breakpoint
|
|
1456
|
+
* (the virtual screen's width / real window), unlike the at-selection snapshot. */
|
|
1457
|
+
computedVal(css) {
|
|
1458
|
+
const el = this.selected;
|
|
1459
|
+
if (!el) return "";
|
|
1460
|
+
if (this._csEl !== el || !this._cs) {
|
|
1461
|
+
try {
|
|
1462
|
+
this._cs = getComputedStyle(el);
|
|
1463
|
+
this._csEl = el;
|
|
1464
|
+
} catch {
|
|
1465
|
+
return "";
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return this._cs.getPropertyValue(css).trim();
|
|
1469
|
+
}
|
|
1316
1470
|
liveVal(css) {
|
|
1317
1471
|
const el = this.selected;
|
|
1318
1472
|
const st = this.st();
|
|
1319
1473
|
if (!el || !st) return "";
|
|
1320
|
-
return el.style.getPropertyValue(css) || st.original[css] || "";
|
|
1474
|
+
return el.style.getPropertyValue(css) || this.computedVal(css) || st.original[css] || "";
|
|
1321
1475
|
}
|
|
1322
1476
|
liveNum(css) {
|
|
1323
1477
|
const v = this.liveVal(css).trim();
|
|
@@ -1403,16 +1557,6 @@ var Uivisor = class {
|
|
|
1403
1557
|
if (this.responsive) return activeBreakpoint(this.frameWidth, sys);
|
|
1404
1558
|
return currentBreakpoint(sys);
|
|
1405
1559
|
}
|
|
1406
|
-
/** Re-tag the selected element's already-recorded changes to the current scope. */
|
|
1407
|
-
retagSelected() {
|
|
1408
|
-
const st = this.st();
|
|
1409
|
-
if (!st) return;
|
|
1410
|
-
const scope = this.activeScope();
|
|
1411
|
-
for (const c of st.record.changes) {
|
|
1412
|
-
c.breakpoint = scope.name;
|
|
1413
|
-
c.breakpointPx = scope.minWidth;
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
1560
|
recordProps(cssList) {
|
|
1417
1561
|
const el = this.selected;
|
|
1418
1562
|
const st = this.st();
|
|
@@ -1492,7 +1636,7 @@ var Uivisor = class {
|
|
|
1492
1636
|
body.innerHTML = `
|
|
1493
1637
|
${this.breakpointBarHtml()}
|
|
1494
1638
|
<div class="uiv-empty">Click any element ${this.responsive ? "in the frame" : "on the page"} to select it.</div>
|
|
1495
|
-
<div class="uiv-hint">Alt+U toggles \xB7 Esc deselects. Tweaks stay in the browser \u2014 nothing is written to your code.</div>
|
|
1639
|
+
<div class="uiv-hint">Alt+U toggles \xB7 Esc deselects \xB7 \u2318/Ctrl+Z undo, \u21E7 to redo. Tweaks stay in the browser \u2014 nothing is written to your code.</div>
|
|
1496
1640
|
${this.journalHtml()}
|
|
1497
1641
|
`;
|
|
1498
1642
|
this.bindControls();
|
|
@@ -1583,8 +1727,9 @@ var Uivisor = class {
|
|
|
1583
1727
|
if (g("box-shadow") !== "none" && g("box-shadow")) add("shadow", "yes");
|
|
1584
1728
|
const op = g("opacity");
|
|
1585
1729
|
if (op && parseFloat(op) < 1) add("opacity", op);
|
|
1586
|
-
const
|
|
1587
|
-
|
|
1730
|
+
const collapsed = this.collapsedSecs.has("Current styles");
|
|
1731
|
+
const items = collapsed ? "" : rows.map((r) => `<div class="uiv-rrow"><span class="uiv-rk">${r.k}</span><span class="uiv-rv${r.edited ? " changed" : ""}">${r.v}</span></div>`).join("");
|
|
1732
|
+
return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
|
|
1588
1733
|
}
|
|
1589
1734
|
/** Breakpoint scope switcher: shows the PROJECT's breakpoints + the live window one. */
|
|
1590
1735
|
breakpointBarHtml() {
|
|
@@ -1640,10 +1785,15 @@ var Uivisor = class {
|
|
|
1640
1785
|
return SECTIONS.map((sec) => {
|
|
1641
1786
|
const controls = sec.controls.filter((c) => this.relevant(c, ctx));
|
|
1642
1787
|
if (!controls.length) return "";
|
|
1643
|
-
const rows = controls.map((c) => this.controlRow(c)).join("");
|
|
1644
|
-
return `<div class="uiv-sec"
|
|
1788
|
+
const rows = this.collapsedSecs.has(sec.title) ? "" : controls.map((c) => this.controlRow(c)).join("");
|
|
1789
|
+
return `<div class="uiv-sec">${this.accordionTitle(sec.title)}${rows}</div>`;
|
|
1645
1790
|
}).join("");
|
|
1646
1791
|
}
|
|
1792
|
+
/** A collapsible section header. Clicking it hides/shows the section's controls. */
|
|
1793
|
+
accordionTitle(title) {
|
|
1794
|
+
const collapsed = this.collapsedSecs.has(title);
|
|
1795
|
+
return `<button class="uiv-sectitle uiv-acc${collapsed ? " collapsed" : ""}" data-sec="${escapeAttr(title)}"><span class="uiv-chev">${ICONS.chevron}</span>${title}</button>`;
|
|
1796
|
+
}
|
|
1647
1797
|
numField(cssAttr, value, handle, changed, isSide, placeholder) {
|
|
1648
1798
|
return `<div class="uiv-num${changed ? " changed" : ""}" data-css="${cssAttr}"><span class="uiv-scrub${isSide ? " txt" : ""}" title="Drag to change">${handle}</span><input type="number" value="${escapeAttr(value)}" placeholder="${escapeAttr(placeholder)}"></div>`;
|
|
1649
1799
|
}
|
|
@@ -1745,7 +1895,8 @@ var Uivisor = class {
|
|
|
1745
1895
|
}
|
|
1746
1896
|
if (c.kind === "select") {
|
|
1747
1897
|
const cur = this.selectCurrent(c.css);
|
|
1748
|
-
const
|
|
1898
|
+
const optList = cur && !c.options.includes(cur) ? [cur, ...c.options] : c.options;
|
|
1899
|
+
const opts = optList.map((o) => `<option value="${o}"${o === cur ? " selected" : ""}>${o}</option>`).join("");
|
|
1749
1900
|
return `<div class="uiv-ctl"><span class="clabel">${c.label}</span><div class="cfield"><select class="uiv-sel${this.isChanged([c.css]) ? " changed" : ""}" data-css="${c.css}">${opts}</select></div><span></span></div>`;
|
|
1750
1901
|
}
|
|
1751
1902
|
const val = toHexInput(this.liveVal(c.css));
|
|
@@ -1758,7 +1909,10 @@ var Uivisor = class {
|
|
|
1758
1909
|
const cssList = (box.getAttribute("data-css") || "").split(",").filter(Boolean);
|
|
1759
1910
|
const input = box.querySelector("input");
|
|
1760
1911
|
const handle = box.querySelector(".uiv-scrub");
|
|
1761
|
-
input.addEventListener("change", () =>
|
|
1912
|
+
input.addEventListener("change", () => {
|
|
1913
|
+
this.pushHistory();
|
|
1914
|
+
this.commitNumeric(cssList, input.value);
|
|
1915
|
+
});
|
|
1762
1916
|
input.addEventListener("keydown", (e) => {
|
|
1763
1917
|
if (e.key === "Enter") input.blur();
|
|
1764
1918
|
});
|
|
@@ -1770,7 +1924,10 @@ var Uivisor = class {
|
|
|
1770
1924
|
const input = box.querySelector("input");
|
|
1771
1925
|
const unitSel = box.querySelector(".uiv-unit");
|
|
1772
1926
|
const handle = box.querySelector(".uiv-scrub");
|
|
1773
|
-
input.addEventListener("change", () =>
|
|
1927
|
+
input.addEventListener("change", () => {
|
|
1928
|
+
this.pushHistory();
|
|
1929
|
+
this.onDimInput(css, box);
|
|
1930
|
+
});
|
|
1774
1931
|
input.addEventListener("keydown", (e) => {
|
|
1775
1932
|
if (e.key === "Enter") input.blur();
|
|
1776
1933
|
});
|
|
@@ -1780,12 +1937,18 @@ var Uivisor = class {
|
|
|
1780
1937
|
root.querySelectorAll(".uiv-color").forEach((node) => {
|
|
1781
1938
|
const input = node;
|
|
1782
1939
|
const css = input.getAttribute("data-css");
|
|
1783
|
-
input.addEventListener("change", () =>
|
|
1940
|
+
input.addEventListener("change", () => {
|
|
1941
|
+
this.pushHistory();
|
|
1942
|
+
this.commitValue([css], input.value);
|
|
1943
|
+
});
|
|
1784
1944
|
});
|
|
1785
1945
|
root.querySelectorAll(".uiv-sel").forEach((node) => {
|
|
1786
1946
|
const sel = node;
|
|
1787
1947
|
const css = sel.getAttribute("data-css");
|
|
1788
|
-
sel.addEventListener("change", () =>
|
|
1948
|
+
sel.addEventListener("change", () => {
|
|
1949
|
+
this.pushHistory();
|
|
1950
|
+
this.commitValue([css], sel.value);
|
|
1951
|
+
});
|
|
1789
1952
|
});
|
|
1790
1953
|
root.querySelectorAll(".uiv-expand").forEach((node) => {
|
|
1791
1954
|
const btn = node;
|
|
@@ -1796,6 +1959,15 @@ var Uivisor = class {
|
|
|
1796
1959
|
this.renderBody();
|
|
1797
1960
|
});
|
|
1798
1961
|
});
|
|
1962
|
+
root.querySelectorAll(".uiv-acc").forEach((node) => {
|
|
1963
|
+
const btn = node;
|
|
1964
|
+
const sec = btn.getAttribute("data-sec");
|
|
1965
|
+
btn.addEventListener("click", () => {
|
|
1966
|
+
if (this.collapsedSecs.has(sec)) this.collapsedSecs.delete(sec);
|
|
1967
|
+
else this.collapsedSecs.add(sec);
|
|
1968
|
+
this.renderBody();
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1799
1971
|
root.querySelectorAll(".uiv-chip").forEach((node) => {
|
|
1800
1972
|
const btn = node;
|
|
1801
1973
|
const bp = btn.getAttribute("data-bp");
|
|
@@ -1811,7 +1983,6 @@ var Uivisor = class {
|
|
|
1811
1983
|
this.toggleResponsive(true);
|
|
1812
1984
|
} else {
|
|
1813
1985
|
this.setFrameWidth(w);
|
|
1814
|
-
this.retagSelected();
|
|
1815
1986
|
this.renderBody();
|
|
1816
1987
|
}
|
|
1817
1988
|
});
|
|
@@ -1821,6 +1992,7 @@ var Uivisor = class {
|
|
|
1821
1992
|
const target = btn.getAttribute("data-target");
|
|
1822
1993
|
btn.addEventListener("click", () => {
|
|
1823
1994
|
const st = this.st();
|
|
1995
|
+
if (st && st.record.target !== target) this.pushHistory();
|
|
1824
1996
|
if (st) st.record.target = target;
|
|
1825
1997
|
this.reapplyForTarget();
|
|
1826
1998
|
this.renderBody();
|
|
@@ -1832,7 +2004,9 @@ var Uivisor = class {
|
|
|
1832
2004
|
const st = this.st();
|
|
1833
2005
|
if (!st) return;
|
|
1834
2006
|
const name = input.value.trim().replace(/^\./, "").replace(/\s+/g, "-");
|
|
1835
|
-
|
|
2007
|
+
const next = name ? `new:${name}` : "element";
|
|
2008
|
+
if (st.record.target !== next) this.pushHistory();
|
|
2009
|
+
st.record.target = next;
|
|
1836
2010
|
this.renderBody();
|
|
1837
2011
|
});
|
|
1838
2012
|
input.addEventListener("keydown", (e) => {
|
|
@@ -1849,7 +2023,12 @@ var Uivisor = class {
|
|
|
1849
2023
|
handle.setPointerCapture(e.pointerId);
|
|
1850
2024
|
} catch {
|
|
1851
2025
|
}
|
|
2026
|
+
let pushed = false;
|
|
1852
2027
|
const move = (ev) => {
|
|
2028
|
+
if (!pushed) {
|
|
2029
|
+
this.pushHistory();
|
|
2030
|
+
pushed = true;
|
|
2031
|
+
}
|
|
1853
2032
|
const dx = ev.clientX - startX;
|
|
1854
2033
|
let nv = start2 + Math.round(dx);
|
|
1855
2034
|
if (ev.shiftKey) nv = Math.round(nv / 10) * 10;
|
|
@@ -1879,7 +2058,12 @@ var Uivisor = class {
|
|
|
1879
2058
|
} catch {
|
|
1880
2059
|
}
|
|
1881
2060
|
const stepFor = (u) => u === "" ? 0.1 : u === "em" ? 0.01 : 1;
|
|
2061
|
+
let pushed = false;
|
|
1882
2062
|
const move = (ev) => {
|
|
2063
|
+
if (!pushed) {
|
|
2064
|
+
this.pushHistory();
|
|
2065
|
+
pushed = true;
|
|
2066
|
+
}
|
|
1883
2067
|
const u = unitSel.value;
|
|
1884
2068
|
const step = stepFor(u) * (ev.shiftKey ? 10 : 1);
|
|
1885
2069
|
const dec = step < 0.1 ? 2 : step < 1 ? 1 : 0;
|
|
@@ -1945,6 +2129,7 @@ var Uivisor = class {
|
|
|
1945
2129
|
if (!el) return;
|
|
1946
2130
|
const st = this.states.get(el);
|
|
1947
2131
|
if (!st) return;
|
|
2132
|
+
if (st.record.changes.length) this.pushHistory();
|
|
1948
2133
|
const sibs = this.siblingsOf(el);
|
|
1949
2134
|
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
1950
2135
|
st.applied.clear();
|
|
@@ -1953,6 +2138,7 @@ var Uivisor = class {
|
|
|
1953
2138
|
this.renderBody();
|
|
1954
2139
|
}
|
|
1955
2140
|
clearAll() {
|
|
2141
|
+
if (this.states.size) this.pushHistory();
|
|
1956
2142
|
for (const [el, st] of this.states) {
|
|
1957
2143
|
const sibs = this.siblingsOf(el);
|
|
1958
2144
|
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
@@ -1962,6 +2148,59 @@ var Uivisor = class {
|
|
|
1962
2148
|
this.reposition();
|
|
1963
2149
|
this.renderBody();
|
|
1964
2150
|
}
|
|
2151
|
+
// ---- undo / redo ----
|
|
2152
|
+
/** Deep snapshot of all edit state (element refs kept; data JSON-cloned). */
|
|
2153
|
+
cloneSnap() {
|
|
2154
|
+
const clone = (v) => JSON.parse(JSON.stringify(v));
|
|
2155
|
+
return {
|
|
2156
|
+
selected: this.selected,
|
|
2157
|
+
entries: [...this.states.entries()].map(([el, st]) => ({
|
|
2158
|
+
el,
|
|
2159
|
+
record: clone(st.record),
|
|
2160
|
+
original: { ...st.original },
|
|
2161
|
+
dimUnit: { ...st.dimUnit }
|
|
2162
|
+
}))
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
/** Record the current state so the next mutation can be undone. */
|
|
2166
|
+
pushHistory() {
|
|
2167
|
+
this.undoStack.push(this.cloneSnap());
|
|
2168
|
+
if (this.undoStack.length > 100) this.undoStack.shift();
|
|
2169
|
+
this.redoStack = [];
|
|
2170
|
+
}
|
|
2171
|
+
/** Rebuild all edit state from a snapshot and re-apply its live overrides. */
|
|
2172
|
+
applySnap(snap) {
|
|
2173
|
+
const clone = (v) => JSON.parse(JSON.stringify(v));
|
|
2174
|
+
for (const [el, st] of this.states) {
|
|
2175
|
+
const sibs = this.siblingsOf(el);
|
|
2176
|
+
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
2177
|
+
}
|
|
2178
|
+
this.states = /* @__PURE__ */ new Map();
|
|
2179
|
+
for (const ent of snap.entries) {
|
|
2180
|
+
const st = { record: clone(ent.record), original: { ...ent.original }, applied: /* @__PURE__ */ new Set(), dimUnit: { ...ent.dimUnit } };
|
|
2181
|
+
this.states.set(ent.el, st);
|
|
2182
|
+
const targets = st.record.target === "all" ? this.siblingsOf(ent.el) : [ent.el];
|
|
2183
|
+
for (const c of st.record.changes) {
|
|
2184
|
+
for (const e of targets) applyOverride(e, c.property, c.after.computed);
|
|
2185
|
+
st.applied.add(c.property);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
this.selected = snap.selected && this.states.has(snap.selected) ? snap.selected : null;
|
|
2189
|
+
this.reposition();
|
|
2190
|
+
this.renderBody();
|
|
2191
|
+
}
|
|
2192
|
+
undo() {
|
|
2193
|
+
if (!this.undoStack.length) return this.showToast("Nothing to undo");
|
|
2194
|
+
this.redoStack.push(this.cloneSnap());
|
|
2195
|
+
this.applySnap(this.undoStack.pop());
|
|
2196
|
+
this.showToast("Undo \u21A9");
|
|
2197
|
+
}
|
|
2198
|
+
redo() {
|
|
2199
|
+
if (!this.redoStack.length) return this.showToast("Nothing to redo");
|
|
2200
|
+
this.undoStack.push(this.cloneSnap());
|
|
2201
|
+
this.applySnap(this.redoStack.pop());
|
|
2202
|
+
this.showToast("Redo \u21AA");
|
|
2203
|
+
}
|
|
1965
2204
|
async copy(text) {
|
|
1966
2205
|
try {
|
|
1967
2206
|
await navigator.clipboard.writeText(text);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uivisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dev-only visual UI tweaker that turns mouse edits into a precise, breakpoint-aware prompt for your AI coding agent — without touching your source.",
|
|
6
6
|
"license": "MIT",
|