uivisor 0.1.5 → 0.1.7
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/overlay/index.js +268 -72
- package/package.json +1 -1
package/dist/overlay/index.js
CHANGED
|
@@ -632,8 +632,12 @@ function nearestToken(ds, category, target) {
|
|
|
632
632
|
const list = ds.byCategory[category];
|
|
633
633
|
if (!list || !list.length) return null;
|
|
634
634
|
if (target.value != null) {
|
|
635
|
-
const norm = (s) =>
|
|
636
|
-
|
|
635
|
+
const norm = (s) => {
|
|
636
|
+
const t = s.trim();
|
|
637
|
+
return (rgbToHex(t) || t).replace(/\s+/g, "").toLowerCase();
|
|
638
|
+
};
|
|
639
|
+
const want = norm(target.value);
|
|
640
|
+
const hit = list.find((t) => norm(t.value) === want);
|
|
637
641
|
if (hit) return { token: hit, exact: true };
|
|
638
642
|
}
|
|
639
643
|
if (target.px != null && list.some((t) => t.px != null)) {
|
|
@@ -1173,6 +1177,12 @@ var CSS = (
|
|
|
1173
1177
|
.uiv-ctl select.uiv-tokensel:hover { border-color: #6366f1; color: #fff; }
|
|
1174
1178
|
.uiv-ctl select.uiv-tokensel:focus { border-color: #818cf8; }
|
|
1175
1179
|
.uiv-ctl select.uiv-tokensel.changed { border-color: #4ade80; color: #86efac; }
|
|
1180
|
+
.uiv-swatches { display: flex; flex-wrap: wrap; gap: 5px; }
|
|
1181
|
+
.uiv-swatch { width: 20px; height: 20px; border-radius: 5px; cursor: pointer;
|
|
1182
|
+
border: 1px solid rgba(255,255,255,0.18); padding: 0; outline: none;
|
|
1183
|
+
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); }
|
|
1184
|
+
.uiv-swatch:hover { transform: scale(1.12); border-color: #fff; }
|
|
1185
|
+
.uiv-swatch.on { box-shadow: 0 0 0 2px #18181b, 0 0 0 3.5px #4ade80; border-color: #4ade80; }
|
|
1176
1186
|
|
|
1177
1187
|
/* ---- current-styles readout ---- */
|
|
1178
1188
|
.uiv-readout { display: flex; flex-direction: column; gap: 3px; }
|
|
@@ -1181,6 +1191,17 @@ var CSS = (
|
|
|
1181
1191
|
.uiv-rk { color: #71717a; }
|
|
1182
1192
|
.uiv-rv { color: #fff; word-break: break-all; display: flex; align-items: center; gap: 6px; }
|
|
1183
1193
|
.uiv-rv.changed { color: #4ade80; } /* edited in uivisor \u2192 green */
|
|
1194
|
+
|
|
1195
|
+
/* control-row state: file (authored) \xB7 edited (this breakpoint) \xB7 auto (computed) */
|
|
1196
|
+
.uiv-ctl.st-file > .clabel { color: #e4e4e7; }
|
|
1197
|
+
.uiv-ctl.st-edited > .clabel { color: #4ade80; }
|
|
1198
|
+
.uiv-ctl.st-auto > .clabel { color: #6b7280; }
|
|
1199
|
+
.uiv-leg { display: flex; gap: 12px; padding: 8px 12px 2px; font-size: 9px;
|
|
1200
|
+
text-transform: uppercase; letter-spacing: .4px; }
|
|
1201
|
+
.uiv-lg { color: #e4e4e7; display: flex; align-items: center; gap: 4px; } /* file = white */
|
|
1202
|
+
.uiv-lg::before { content: ''; width: 7px; height: 7px; border-radius: 2px; background: currentColor; }
|
|
1203
|
+
.uiv-lg.edit { color: #4ade80; }
|
|
1204
|
+
.uiv-lg.auto { color: #6b7280; }
|
|
1184
1205
|
.uiv-sw { display: inline-block; width: 11px; height: 11px; border-radius: 3px;
|
|
1185
1206
|
border: 1px solid rgba(255,255,255,0.2); flex: 0 0 auto; }
|
|
1186
1207
|
|
|
@@ -1282,6 +1303,15 @@ var CSS = (
|
|
|
1282
1303
|
// src/overlay/index.ts
|
|
1283
1304
|
var counter = 0;
|
|
1284
1305
|
var round2 = (n) => Math.round(n * 100) / 100;
|
|
1306
|
+
var INHERITED_PROPS = /* @__PURE__ */ new Set([
|
|
1307
|
+
"font-size",
|
|
1308
|
+
"font-weight",
|
|
1309
|
+
"line-height",
|
|
1310
|
+
"letter-spacing",
|
|
1311
|
+
"color",
|
|
1312
|
+
"text-align",
|
|
1313
|
+
"font-family"
|
|
1314
|
+
]);
|
|
1285
1315
|
var Uivisor = class {
|
|
1286
1316
|
constructor() {
|
|
1287
1317
|
this.enabled = false;
|
|
@@ -1293,9 +1323,11 @@ var Uivisor = class {
|
|
|
1293
1323
|
/** Undo / redo stacks of full edit-state snapshots. */
|
|
1294
1324
|
this.undoStack = [];
|
|
1295
1325
|
this.redoStack = [];
|
|
1296
|
-
/** Cached live computed-style for the selected element (invalidated
|
|
1326
|
+
/** Cached live computed-style for the selected element (invalidated each render). */
|
|
1297
1327
|
this._cs = null;
|
|
1298
1328
|
this._csEl = null;
|
|
1329
|
+
/** Per-render memo of authored longhands per element (for the styles readout). */
|
|
1330
|
+
this._matched = null;
|
|
1299
1331
|
/** Cached project breakpoint system (detected from CSS), refreshed until found. */
|
|
1300
1332
|
this._bp = null;
|
|
1301
1333
|
/** Cached project design system (detected from CSS variables), refreshed until found. */
|
|
@@ -1360,7 +1392,10 @@ var Uivisor = class {
|
|
|
1360
1392
|
const winBp = currentBreakpoint(this.bpSystem()).name;
|
|
1361
1393
|
if (winBp !== this.lastWinBp) {
|
|
1362
1394
|
this.lastWinBp = winBp;
|
|
1363
|
-
if (this.enabled && this.selected)
|
|
1395
|
+
if (this.enabled && this.selected) {
|
|
1396
|
+
this.reapplyScope();
|
|
1397
|
+
this.renderBody();
|
|
1398
|
+
}
|
|
1364
1399
|
}
|
|
1365
1400
|
};
|
|
1366
1401
|
this.reposition = () => {
|
|
@@ -1626,6 +1661,7 @@ var Uivisor = class {
|
|
|
1626
1661
|
const up = () => {
|
|
1627
1662
|
handle.removeEventListener("pointermove", move);
|
|
1628
1663
|
handle.removeEventListener("pointerup", up);
|
|
1664
|
+
this.reapplyScope();
|
|
1629
1665
|
this.renderBody();
|
|
1630
1666
|
};
|
|
1631
1667
|
handle.addEventListener("pointermove", move);
|
|
@@ -1661,6 +1697,7 @@ var Uivisor = class {
|
|
|
1661
1697
|
dimUnit: {}
|
|
1662
1698
|
});
|
|
1663
1699
|
}
|
|
1700
|
+
if (el) this.reapplyScope();
|
|
1664
1701
|
this.reposition();
|
|
1665
1702
|
this.renderBody();
|
|
1666
1703
|
}
|
|
@@ -1687,7 +1724,15 @@ var Uivisor = class {
|
|
|
1687
1724
|
const el = this.selected;
|
|
1688
1725
|
const st = this.st();
|
|
1689
1726
|
if (!el || !st) return "";
|
|
1690
|
-
|
|
1727
|
+
const scope = this.activeScope();
|
|
1728
|
+
const ch = st.record.changes.find((c) => c.property === css && c.breakpoint === scope.name);
|
|
1729
|
+
if (ch) {
|
|
1730
|
+
const v = ch.live ?? ch.after.computed;
|
|
1731
|
+
return v.includes("var(") ? this.computedVal(css) || v : v;
|
|
1732
|
+
}
|
|
1733
|
+
const inline = el.style.getPropertyValue(css);
|
|
1734
|
+
if (inline && !inline.includes("var(")) return inline;
|
|
1735
|
+
return this.computedVal(css) || st.original[css] || "";
|
|
1691
1736
|
}
|
|
1692
1737
|
liveNum(css) {
|
|
1693
1738
|
const v = this.liveVal(css).trim();
|
|
@@ -1707,7 +1752,10 @@ var Uivisor = class {
|
|
|
1707
1752
|
isChanged(cssList) {
|
|
1708
1753
|
const st = this.st();
|
|
1709
1754
|
if (!st) return false;
|
|
1710
|
-
|
|
1755
|
+
const scope = this.activeScope();
|
|
1756
|
+
return cssList.some(
|
|
1757
|
+
(c) => st.record.changes.some((ch) => ch.property === c && ch.breakpoint === scope.name)
|
|
1758
|
+
);
|
|
1711
1759
|
}
|
|
1712
1760
|
selectCurrent(css) {
|
|
1713
1761
|
let v = this.liveVal(css).trim();
|
|
@@ -1752,16 +1800,27 @@ var Uivisor = class {
|
|
|
1752
1800
|
}
|
|
1753
1801
|
/** Re-apply recorded overrides after the target (all ↔ one) changes. */
|
|
1754
1802
|
reapplyForTarget() {
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1803
|
+
this.reapplyScope();
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Project the live preview for the ACTIVE breakpoint: strip every override we
|
|
1807
|
+
* applied, then re-apply ONLY the changes recorded for the current scope. This
|
|
1808
|
+
* is what makes per-breakpoint edits behave — set padding 20 at xl, 10 at md,
|
|
1809
|
+
* and each breakpoint shows (and previews) its own value instead of one global
|
|
1810
|
+
* inline override leaking across all of them.
|
|
1811
|
+
*/
|
|
1812
|
+
reapplyScope() {
|
|
1813
|
+
const scope = this.activeScope();
|
|
1814
|
+
for (const [el, st] of this.states) {
|
|
1815
|
+
const sibs = this.siblingsOf(el);
|
|
1816
|
+
const targets = st.record.target === "all" ? sibs : [el];
|
|
1817
|
+
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
1818
|
+
st.applied = /* @__PURE__ */ new Set();
|
|
1819
|
+
for (const c of st.record.changes) {
|
|
1820
|
+
if (c.breakpoint !== scope.name) continue;
|
|
1821
|
+
for (const e of targets) applyOverride(e, c.property, c.live ?? c.after.computed);
|
|
1822
|
+
st.applied.add(c.property);
|
|
1823
|
+
}
|
|
1765
1824
|
}
|
|
1766
1825
|
this.reposition();
|
|
1767
1826
|
}
|
|
@@ -1786,20 +1845,22 @@ var Uivisor = class {
|
|
|
1786
1845
|
}
|
|
1787
1846
|
this.renderBody();
|
|
1788
1847
|
}
|
|
1789
|
-
/** Apply a design-system token to a property
|
|
1790
|
-
*
|
|
1848
|
+
/** Apply a design-system token to a property. The LIVE override is `var(--token)`
|
|
1849
|
+
* so a responsive token keeps adapting as you move across breakpoints; the
|
|
1850
|
+
* recorded value shows the token's value, tagged so the prompt asks for the token. */
|
|
1791
1851
|
applyToken(css, token) {
|
|
1792
1852
|
this.pushHistory();
|
|
1793
|
-
|
|
1794
|
-
this.
|
|
1853
|
+
const live = `var(${token.cssVar})`;
|
|
1854
|
+
this.liveSet([css], live);
|
|
1795
1855
|
const st = this.st();
|
|
1796
1856
|
if (!st) return;
|
|
1797
1857
|
const scope = this.activeScope();
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1858
|
+
st.applied.add(css);
|
|
1859
|
+
const change = buildChange(css, st.original[css], token.value, scope);
|
|
1860
|
+
change.live = live;
|
|
1861
|
+
change.after.designToken = token.cssVar;
|
|
1862
|
+
change.after.token = st.record.styling.primaryMechanism === "tailwind" ? this.dsUtility(css, token) : null;
|
|
1863
|
+
this.setChange(st.record, change);
|
|
1803
1864
|
this.renderBody();
|
|
1804
1865
|
}
|
|
1805
1866
|
revertProps(cssList) {
|
|
@@ -1863,6 +1924,9 @@ var Uivisor = class {
|
|
|
1863
1924
|
}
|
|
1864
1925
|
// ---- rendering ----
|
|
1865
1926
|
renderBody() {
|
|
1927
|
+
this._cs = null;
|
|
1928
|
+
this._csEl = null;
|
|
1929
|
+
this._matched = null;
|
|
1866
1930
|
const body = this.q(".uiv-body");
|
|
1867
1931
|
if (!this.selected) {
|
|
1868
1932
|
body.innerHTML = `
|
|
@@ -1899,7 +1963,79 @@ var Uivisor = class {
|
|
|
1899
1963
|
const cats = Object.keys(ds.byCategory).map((c) => `${ds.byCategory[c].length} ${c}`).join(" \xB7 ");
|
|
1900
1964
|
return `<div class="uiv-dsbar" title="${escapeAttr(cats)}">\u25C6 Design system \xB7 ${ds.tokens.length} tokens detected</div>`;
|
|
1901
1965
|
}
|
|
1902
|
-
/**
|
|
1966
|
+
/** Longhand properties an authoring rule (or non-uivisor inline) sets on `el`.
|
|
1967
|
+
* Reimplements getMatchedCSSRules over same-origin sheets (incl. matching
|
|
1968
|
+
* @media), so we can tell "set in the project's CSS" from "browser default". */
|
|
1969
|
+
matchedProps(el) {
|
|
1970
|
+
const cache = this._matched ||= /* @__PURE__ */ new Map();
|
|
1971
|
+
const cached = cache.get(el);
|
|
1972
|
+
if (cached) return cached;
|
|
1973
|
+
const out = /* @__PURE__ */ new Set();
|
|
1974
|
+
const he = el;
|
|
1975
|
+
const applied = el === this.selected ? this.st()?.applied ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
|
|
1976
|
+
if (he.style) for (let i = 0; i < he.style.length; i++) {
|
|
1977
|
+
const p = he.style[i];
|
|
1978
|
+
if (!applied.has(p)) out.add(p);
|
|
1979
|
+
}
|
|
1980
|
+
const win = el.ownerDocument.defaultView || window;
|
|
1981
|
+
const walk = (rules) => {
|
|
1982
|
+
for (let i = 0; i < rules.length; i++) {
|
|
1983
|
+
const r = rules[i];
|
|
1984
|
+
if (r.selectorText && r.style) {
|
|
1985
|
+
let m = false;
|
|
1986
|
+
try {
|
|
1987
|
+
m = el.matches(r.selectorText);
|
|
1988
|
+
} catch {
|
|
1989
|
+
m = false;
|
|
1990
|
+
}
|
|
1991
|
+
if (m) for (let j = 0; j < r.style.length; j++) out.add(r.style[j]);
|
|
1992
|
+
} else if (r.media && r.cssRules) {
|
|
1993
|
+
let ok = true;
|
|
1994
|
+
try {
|
|
1995
|
+
ok = win.matchMedia(r.media.mediaText).matches;
|
|
1996
|
+
} catch {
|
|
1997
|
+
ok = true;
|
|
1998
|
+
}
|
|
1999
|
+
if (ok) walk(r.cssRules);
|
|
2000
|
+
} else if (r.cssRules) {
|
|
2001
|
+
walk(r.cssRules);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
const sheets = el.ownerDocument.styleSheets;
|
|
2006
|
+
for (let i = 0; i < sheets.length; i++) {
|
|
2007
|
+
try {
|
|
2008
|
+
walk(sheets[i].cssRules);
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
cache.set(el, out);
|
|
2013
|
+
return out;
|
|
2014
|
+
}
|
|
2015
|
+
/** State class for an editable control's row: edited (green, at this breakpoint)
|
|
2016
|
+
* · file (white, authored in CSS) · auto (grey, browser-computed/default). */
|
|
2017
|
+
controlStateClass(props) {
|
|
2018
|
+
if (this.isChanged(props)) return " st-edited";
|
|
2019
|
+
const inherit = props.some((p) => INHERITED_PROPS.has(p));
|
|
2020
|
+
return this.isAuthored(props, inherit) ? " st-file" : " st-auto";
|
|
2021
|
+
}
|
|
2022
|
+
/** Is any of `props` authored in the project CSS? For inherited properties we
|
|
2023
|
+
* also walk ancestors (a body/parent font rule still counts as "from the file"). */
|
|
2024
|
+
isAuthored(props, inherit) {
|
|
2025
|
+
let node = this.selected;
|
|
2026
|
+
while (node) {
|
|
2027
|
+
const set = this.matchedProps(node);
|
|
2028
|
+
if (props.some((p) => set.has(p))) return true;
|
|
2029
|
+
node = inherit ? node.parentElement : null;
|
|
2030
|
+
}
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Read-only readout of the element's ACTUAL current styles, three-state coloured:
|
|
2035
|
+
* white = authored in the project CSS · green = edited in uivisor ·
|
|
2036
|
+
* grey = browser-computed/auto (e.g. a width a flex item derived, a default).
|
|
2037
|
+
* Comprehensive + context-aware (flex/grid/position rows only when relevant).
|
|
2038
|
+
*/
|
|
1903
2039
|
currentStylesHtml() {
|
|
1904
2040
|
const el = this.selected;
|
|
1905
2041
|
if (!el) return "";
|
|
@@ -1915,47 +2051,70 @@ var Uivisor = class {
|
|
|
1915
2051
|
const h = hex(v);
|
|
1916
2052
|
return /^#|rgb/.test(h) ? `<span class="uiv-sw" style="background:${h}"></span>${h}` : h;
|
|
1917
2053
|
};
|
|
1918
|
-
const
|
|
1919
|
-
const t = g
|
|
1920
|
-
const r = g(`${prefix}-right`);
|
|
1921
|
-
const b = g(`${prefix}-bottom`);
|
|
1922
|
-
const l = g(`${prefix}-left`);
|
|
2054
|
+
const sides = (parts) => {
|
|
2055
|
+
const [t, r, b, l] = parts.map(g);
|
|
1923
2056
|
if (t === r && r === b && b === l) return t;
|
|
1924
2057
|
if (t === b && r === l) return `${t} ${r}`;
|
|
1925
2058
|
return `${t} ${r} ${b} ${l}`;
|
|
1926
2059
|
};
|
|
1927
|
-
const
|
|
1928
|
-
const
|
|
1929
|
-
const px4 = (pre) => [`${pre}-top`, `${pre}-right`, `${pre}-bottom`, `${pre}-left`];
|
|
2060
|
+
const px4 = (pre, suf = "") => [`${pre}-top${suf}`, `${pre}-right${suf}`, `${pre}-bottom${suf}`, `${pre}-left${suf}`];
|
|
2061
|
+
const clip = (s, n = 30) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
1930
2062
|
const rows = [];
|
|
1931
|
-
const add = (k, v,
|
|
1932
|
-
if (v) rows.push({ k, v
|
|
2063
|
+
const add = (k, v, _props = [], _inherit = false) => {
|
|
2064
|
+
if (v !== "" && v != null) rows.push({ k, v });
|
|
1933
2065
|
};
|
|
1934
|
-
|
|
1935
|
-
add("
|
|
1936
|
-
const
|
|
1937
|
-
if (
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2066
|
+
const disp = g("display");
|
|
2067
|
+
add("display", disp, ["display"]);
|
|
2068
|
+
const pos = g("position");
|
|
2069
|
+
if (pos && pos !== "static") {
|
|
2070
|
+
add("position", pos, ["position"]);
|
|
2071
|
+
const inset = ["top", "right", "bottom", "left"].filter((s) => g(s) !== "auto").map((s) => `${s[0]} ${g(s)}`).join(" ");
|
|
2072
|
+
if (inset) add("inset", inset, ["top", "right", "bottom", "left"]);
|
|
2073
|
+
}
|
|
2074
|
+
add("width", `${Math.round(parseFloat(g("width")) || 0)}px`, ["width"]);
|
|
2075
|
+
add("height", `${Math.round(parseFloat(g("height")) || 0)}px`, ["height"]);
|
|
2076
|
+
const maxw = g("max-width");
|
|
2077
|
+
if (maxw && maxw !== "none") add("max-w", maxw, ["max-width"]);
|
|
2078
|
+
const minh = g("min-height");
|
|
2079
|
+
if (minh && minh !== "0px" && minh !== "auto") add("min-h", minh, ["min-height"]);
|
|
2080
|
+
if (/flex|grid/.test(disp)) {
|
|
2081
|
+
if (disp.includes("flex")) add("direction", g("flex-direction"), ["flex-direction"]);
|
|
2082
|
+
add("justify", g("justify-content"), ["justify-content"]);
|
|
2083
|
+
add("align", g("align-items"), ["align-items"]);
|
|
2084
|
+
if (disp.includes("flex")) {
|
|
2085
|
+
const w = g("flex-wrap");
|
|
2086
|
+
if (w && w !== "nowrap") add("wrap", w, ["flex-wrap"]);
|
|
2087
|
+
}
|
|
2088
|
+
if (disp.includes("grid")) {
|
|
2089
|
+
const c = g("grid-template-columns");
|
|
2090
|
+
if (c && c !== "none") add("grid-cols", clip(c), ["grid-template-columns"]);
|
|
2091
|
+
}
|
|
1941
2092
|
const gap = g("gap");
|
|
1942
|
-
if (gap && gap !== "normal"
|
|
2093
|
+
if (gap && gap !== "normal") add("gap", gap, ["gap", "row-gap", "column-gap"]);
|
|
1943
2094
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2095
|
+
let parentDisp = "";
|
|
2096
|
+
try {
|
|
2097
|
+
if (el.parentElement) parentDisp = getComputedStyle(el.parentElement).display;
|
|
2098
|
+
} catch {
|
|
2099
|
+
}
|
|
2100
|
+
if (/flex|grid/.test(parentDisp)) {
|
|
2101
|
+
add("flex", `${g("flex-grow")} ${g("flex-shrink")} ${g("flex-basis")}`, ["flex-grow", "flex-shrink", "flex-basis"]);
|
|
2102
|
+
const as = g("align-self");
|
|
2103
|
+
if (as && as !== "auto" && as !== "normal") add("self", as, ["align-self"]);
|
|
2104
|
+
}
|
|
2105
|
+
const pad = sides(px4("padding"));
|
|
2106
|
+
if (pad && pad !== "0px") add("padding", pad, px4("padding"));
|
|
2107
|
+
const mar = sides(px4("margin"));
|
|
2108
|
+
if (mar && mar !== "0px") add("margin", mar, px4("margin"));
|
|
1955
2109
|
const bw = g("border-top-width");
|
|
1956
2110
|
if (bw && parseFloat(bw) > 0)
|
|
1957
|
-
add("border", `${bw} ${g("border-top-style")} ${
|
|
1958
|
-
const br =
|
|
2111
|
+
add("border", `${bw} ${g("border-top-style")} ${swatch(g("border-top-color"))}`, px4("border", "-width"));
|
|
2112
|
+
const br = sides([
|
|
2113
|
+
"border-top-left-radius",
|
|
2114
|
+
"border-top-right-radius",
|
|
2115
|
+
"border-bottom-right-radius",
|
|
2116
|
+
"border-bottom-left-radius"
|
|
2117
|
+
]);
|
|
1959
2118
|
if (br && br !== "0px")
|
|
1960
2119
|
add("radius", br, [
|
|
1961
2120
|
"border-radius",
|
|
@@ -1964,11 +2123,32 @@ var Uivisor = class {
|
|
|
1964
2123
|
"border-bottom-right-radius",
|
|
1965
2124
|
"border-bottom-left-radius"
|
|
1966
2125
|
]);
|
|
1967
|
-
if (
|
|
2126
|
+
if (this.context(el).hasText) {
|
|
2127
|
+
add("font-size", g("font-size"), ["font-size"], true);
|
|
2128
|
+
add("weight", g("font-weight"), ["font-weight"], true);
|
|
2129
|
+
add("line", g("line-height"), ["line-height"], true);
|
|
2130
|
+
const ls = g("letter-spacing");
|
|
2131
|
+
if (ls && ls !== "normal") add("tracking", ls, ["letter-spacing"], true);
|
|
2132
|
+
add("color", swatch(g("color")), ["color"], true);
|
|
2133
|
+
const ta = g("text-align");
|
|
2134
|
+
if (ta && ta !== "start" && ta !== "left") add("text-align", ta, ["text-align"], true);
|
|
2135
|
+
}
|
|
2136
|
+
const bg = g("background-color");
|
|
2137
|
+
if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") add("background", swatch(bg), ["background-color"]);
|
|
2138
|
+
const bgi = g("background-image");
|
|
2139
|
+
if (bgi && bgi !== "none") add("bg-image", clip(bgi, 26), ["background-image"]);
|
|
2140
|
+
const sh = g("box-shadow");
|
|
2141
|
+
if (sh && sh !== "none") add("shadow", clip(sh, 26), ["box-shadow"]);
|
|
1968
2142
|
const op = g("opacity");
|
|
1969
|
-
if (op && parseFloat(op) < 1) add("opacity", op);
|
|
2143
|
+
if (op && parseFloat(op) < 1) add("opacity", op, ["opacity"]);
|
|
2144
|
+
const ov = g("overflow");
|
|
2145
|
+
if (ov && ov !== "visible") add("overflow", ov, ["overflow", "overflow-x", "overflow-y"]);
|
|
2146
|
+
const z = g("z-index");
|
|
2147
|
+
if (z && z !== "auto") add("z-index", z, ["z-index"]);
|
|
2148
|
+
const tr = g("transform");
|
|
2149
|
+
if (tr && tr !== "none") add("transform", clip(tr, 22), ["transform"]);
|
|
1970
2150
|
const collapsed = this.collapsedSecs.has("Current styles");
|
|
1971
|
-
const items = collapsed ? "" : rows.map((r) => `<div class="uiv-rrow"><span class="uiv-rk">${r.k}</span><span class="uiv-rv
|
|
2151
|
+
const items = collapsed ? "" : rows.map((r) => `<div class="uiv-rrow"><span class="uiv-rk">${r.k}</span><span class="uiv-rv">${r.v}</span></div>`).join("");
|
|
1972
2152
|
return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
|
|
1973
2153
|
}
|
|
1974
2154
|
/** Breakpoint scope switcher: shows the PROJECT's breakpoints + the live window one. */
|
|
@@ -2022,12 +2202,14 @@ var Uivisor = class {
|
|
|
2022
2202
|
return true;
|
|
2023
2203
|
}
|
|
2024
2204
|
controlsHtml(ctx) {
|
|
2025
|
-
|
|
2205
|
+
const legend = `<div class="uiv-leg"><span class="uiv-lg">file</span><span class="uiv-lg edit">edited</span><span class="uiv-lg auto">auto</span></div>`;
|
|
2206
|
+
const secs = SECTIONS.map((sec) => {
|
|
2026
2207
|
const controls = sec.controls.filter((c) => this.relevant(c, ctx));
|
|
2027
2208
|
if (!controls.length) return "";
|
|
2028
2209
|
const rows = this.collapsedSecs.has(sec.title) ? "" : controls.map((c) => this.controlRow(c)).join("");
|
|
2029
2210
|
return `<div class="uiv-sec">${this.accordionTitle(sec.title)}${rows}</div>`;
|
|
2030
2211
|
}).join("");
|
|
2212
|
+
return legend + secs;
|
|
2031
2213
|
}
|
|
2032
2214
|
/** A collapsible section header. Clicking it hides/shows the section's controls. */
|
|
2033
2215
|
accordionTitle(title) {
|
|
@@ -2102,7 +2284,7 @@ var Uivisor = class {
|
|
|
2102
2284
|
const d = this.dimDisplay(c);
|
|
2103
2285
|
const changed = this.isChanged([c.css]);
|
|
2104
2286
|
const units = c.units.map((u) => `<option value="${u}"${u === d.unit ? " selected" : ""}>${UNIT_LABELS[u] ?? u}</option>`).join("");
|
|
2105
|
-
return `<div class="uiv-ctl"><span class="clabel">${c.label}</span><div class="cfield"><div class="uiv-num uiv-dim${changed ? " changed" : ""}" data-css="${c.css}"><span class="uiv-scrub" title="Drag to change">${c.icon}</span><input type="number" step="any" value="${escapeAttr(d.num)}" placeholder="${escapeAttr(d.placeholder)}"><select class="uiv-unit" title="Unit">${units}</select></div></div><span></span></div>`;
|
|
2287
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"><span class="clabel">${c.label}</span><div class="cfield"><div class="uiv-num uiv-dim${changed ? " changed" : ""}" data-css="${c.css}"><span class="uiv-scrub" title="Drag to change">${c.icon}</span><input type="number" step="any" value="${escapeAttr(d.num)}" placeholder="${escapeAttr(d.placeholder)}"><select class="uiv-unit" title="Unit">${units}</select></div></div><span></span></div>`;
|
|
2106
2288
|
}
|
|
2107
2289
|
/** A design-token picker row for a property, shown only when the project exposes
|
|
2108
2290
|
* tokens for that category. Picking a token applies its value + tags the prompt. */
|
|
@@ -2117,6 +2299,13 @@ var Uivisor = class {
|
|
|
2117
2299
|
const edited = this.st()?.record.changes.some(
|
|
2118
2300
|
(c) => c.property === css && c.after.designToken
|
|
2119
2301
|
);
|
|
2302
|
+
if (cat === "color") {
|
|
2303
|
+
const swatches = list.map((t) => {
|
|
2304
|
+
const on = near?.exact && near.token.cssVar === t.cssVar;
|
|
2305
|
+
return `<button class="uiv-swatch${on ? " on" : ""}" data-css="${css}" data-var="${escapeAttr(t.cssVar)}" title="${escapeAttr(`${t.name} \xB7 ${t.value}`)}" style="background:${escapeAttr(t.value)}"></button>`;
|
|
2306
|
+
}).join("");
|
|
2307
|
+
return `<div class="uiv-ctl"><span class="clabel uiv-tlabel">${label}</span><div class="cfield uiv-swatches">${swatches}</div><span></span></div>`;
|
|
2308
|
+
}
|
|
2120
2309
|
const head = `<option value="">${near && !near.exact ? `\u2248 ${escapeHtml(near.token.name)} \xB7 pick token` : "\u2014 pick token \u2014"}</option>`;
|
|
2121
2310
|
const opts = list.map(
|
|
2122
2311
|
(t) => `<option value="${escapeAttr(t.cssVar)}"${near?.exact && near.token.cssVar === t.cssVar ? " selected" : ""}>${escapeHtml(`${t.name} \xB7 ${t.value}`)}</option>`
|
|
@@ -2129,7 +2318,7 @@ var Uivisor = class {
|
|
|
2129
2318
|
const info = this.numInfo(cssList);
|
|
2130
2319
|
const changed = this.isChanged(cssList);
|
|
2131
2320
|
const open = this.expanded.has(c.key);
|
|
2132
|
-
let html = `<div class="uiv-ctl"><span class="clabel">${c.label}</span><div class="cfield">${this.numField(cssList.join(","), info.mixed ? "" : info.value, c.icon, changed, false, info.mixed ? "Mixed" : "\u2014")}</div><button class="uiv-expand${open ? " on" : ""}" data-key="${c.key}" title="Edit each side individually">${open ? ICONS.collapse : ICONS.expand}</button></div>`;
|
|
2321
|
+
let html = `<div class="uiv-ctl${this.controlStateClass(cssList)}"><span class="clabel">${c.label}</span><div class="cfield">${this.numField(cssList.join(","), info.mixed ? "" : info.value, c.icon, changed, false, info.mixed ? "Mixed" : "\u2014")}</div><button class="uiv-expand${open ? " on" : ""}" data-key="${c.key}" title="Edit each side individually">${open ? ICONS.collapse : ICONS.expand}</button></div>`;
|
|
2133
2322
|
if (open) {
|
|
2134
2323
|
html += `<div class="uiv-sides">` + c.sides.map((s) => {
|
|
2135
2324
|
const v = this.liveNum(s.css);
|
|
@@ -2147,7 +2336,7 @@ var Uivisor = class {
|
|
|
2147
2336
|
}
|
|
2148
2337
|
if (c.kind === "len") {
|
|
2149
2338
|
const v = this.liveNum(c.css);
|
|
2150
|
-
return `<div class="uiv-ctl"><span class="clabel">${c.label}</span><div class="cfield">${this.numField(c.css, v == null ? "" : String(round2(v)), c.icon, this.isChanged([c.css]), false, "\u2014")}</div><span></span></div>` + this.tokenRowHtml(c.css, "Token");
|
|
2339
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"><span class="clabel">${c.label}</span><div class="cfield">${this.numField(c.css, v == null ? "" : String(round2(v)), c.icon, this.isChanged([c.css]), false, "\u2014")}</div><span></span></div>` + this.tokenRowHtml(c.css, "Token");
|
|
2151
2340
|
}
|
|
2152
2341
|
if (c.kind === "dim") {
|
|
2153
2342
|
return this.dimField(c);
|
|
@@ -2156,10 +2345,10 @@ var Uivisor = class {
|
|
|
2156
2345
|
const cur = this.selectCurrent(c.css);
|
|
2157
2346
|
const optList = cur && !c.options.includes(cur) ? [cur, ...c.options] : c.options;
|
|
2158
2347
|
const opts = optList.map((o) => `<option value="${o}"${o === cur ? " selected" : ""}>${o}</option>`).join("");
|
|
2159
|
-
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>`;
|
|
2348
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"><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>`;
|
|
2160
2349
|
}
|
|
2161
2350
|
const val = toHexInput(this.liveVal(c.css));
|
|
2162
|
-
return `<div class="uiv-ctl"><span class="clabel">${c.label}</span><div class="cfield"><input type="color" class="uiv-color${this.isChanged([c.css]) ? " changed" : ""}" data-css="${c.css}" value="${val}"></div><span></span></div>` + this.tokenRowHtml(c.css, "Token");
|
|
2351
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"><span class="clabel">${c.label}</span><div class="cfield"><input type="color" class="uiv-color${this.isChanged([c.css]) ? " changed" : ""}" data-css="${c.css}" value="${val}"></div><span></span></div>` + this.tokenRowHtml(c.css, "Token");
|
|
2163
2352
|
}
|
|
2164
2353
|
bindControls() {
|
|
2165
2354
|
const root = this.root;
|
|
@@ -2218,6 +2407,15 @@ var Uivisor = class {
|
|
|
2218
2407
|
if (token) this.applyToken(css, token);
|
|
2219
2408
|
});
|
|
2220
2409
|
});
|
|
2410
|
+
root.querySelectorAll(".uiv-swatch").forEach((node) => {
|
|
2411
|
+
const btn = node;
|
|
2412
|
+
const css = btn.getAttribute("data-css");
|
|
2413
|
+
const cssVar = btn.getAttribute("data-var");
|
|
2414
|
+
btn.addEventListener("click", () => {
|
|
2415
|
+
const token = this.designSystem().tokens.find((t) => t.cssVar === cssVar);
|
|
2416
|
+
if (token) this.applyToken(css, token);
|
|
2417
|
+
});
|
|
2418
|
+
});
|
|
2221
2419
|
root.querySelectorAll(".uiv-expand").forEach((node) => {
|
|
2222
2420
|
const btn = node;
|
|
2223
2421
|
const key = btn.getAttribute("data-key");
|
|
@@ -2251,6 +2449,7 @@ var Uivisor = class {
|
|
|
2251
2449
|
this.toggleResponsive(true);
|
|
2252
2450
|
} else {
|
|
2253
2451
|
this.setFrameWidth(w);
|
|
2452
|
+
this.reapplyScope();
|
|
2254
2453
|
this.renderBody();
|
|
2255
2454
|
}
|
|
2256
2455
|
});
|
|
@@ -2364,7 +2563,8 @@ var Uivisor = class {
|
|
|
2364
2563
|
const loc = id.source.confidence !== "none" ? `${id.source.file}:${id.source.line}` : id.componentName || "";
|
|
2365
2564
|
const chgs = collapseChanges(r.changes).map((c) => {
|
|
2366
2565
|
const bp = c.breakpoint === "base" ? "" : `<span class="bp">${c.breakpoint}:</span> `;
|
|
2367
|
-
const
|
|
2566
|
+
const tokLabel = c.after.token || (c.after.designToken ? `var(${c.after.designToken})` : "");
|
|
2567
|
+
const tok = tokLabel ? ` <span class="tok">${escapeHtml(tokLabel)}</span>` : "";
|
|
2368
2568
|
return `<div class="uiv-jchg">${bp}${c.property}: ${c.before.computed} \u2192 ${c.after.computed}${tok}</div>`;
|
|
2369
2569
|
}).join("");
|
|
2370
2570
|
return `<div class="uiv-jitem"><div class="jhead"><span class="jel"><${id.tagName}></span><span class="jloc">${escapeHtml(loc)}</span></div>${chgs}</div>`;
|
|
@@ -2447,13 +2647,9 @@ var Uivisor = class {
|
|
|
2447
2647
|
for (const ent of snap.entries) {
|
|
2448
2648
|
const st = { record: clone(ent.record), original: { ...ent.original }, applied: /* @__PURE__ */ new Set(), dimUnit: { ...ent.dimUnit } };
|
|
2449
2649
|
this.states.set(ent.el, st);
|
|
2450
|
-
const targets = st.record.target === "all" ? this.siblingsOf(ent.el) : [ent.el];
|
|
2451
|
-
for (const c of st.record.changes) {
|
|
2452
|
-
for (const e of targets) applyOverride(e, c.property, c.after.computed);
|
|
2453
|
-
st.applied.add(c.property);
|
|
2454
|
-
}
|
|
2455
2650
|
}
|
|
2456
2651
|
this.selected = snap.selected && this.states.has(snap.selected) ? snap.selected : null;
|
|
2652
|
+
this.reapplyScope();
|
|
2457
2653
|
this.reposition();
|
|
2458
2654
|
this.renderBody();
|
|
2459
2655
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uivisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|