uivisor 0.1.6 → 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.
@@ -1191,8 +1191,12 @@ var CSS = (
1191
1191
  .uiv-rk { color: #71717a; }
1192
1192
  .uiv-rv { color: #fff; word-break: break-all; display: flex; align-items: center; gap: 6px; }
1193
1193
  .uiv-rv.changed { color: #4ade80; } /* edited in uivisor \u2192 green */
1194
- .uiv-rv.auto { color: #6b7280; } /* browser-computed / default \u2192 grey */
1195
- .uiv-leg { display: flex; gap: 10px; padding: 1px 0 6px; font-size: 9px;
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;
1196
1200
  text-transform: uppercase; letter-spacing: .4px; }
1197
1201
  .uiv-lg { color: #e4e4e7; display: flex; align-items: center; gap: 4px; } /* file = white */
1198
1202
  .uiv-lg::before { content: ''; width: 7px; height: 7px; border-radius: 2px; background: currentColor; }
@@ -1299,6 +1303,15 @@ var CSS = (
1299
1303
  // src/overlay/index.ts
1300
1304
  var counter = 0;
1301
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
+ ]);
1302
1315
  var Uivisor = class {
1303
1316
  constructor() {
1304
1317
  this.enabled = false;
@@ -1379,7 +1392,10 @@ var Uivisor = class {
1379
1392
  const winBp = currentBreakpoint(this.bpSystem()).name;
1380
1393
  if (winBp !== this.lastWinBp) {
1381
1394
  this.lastWinBp = winBp;
1382
- if (this.enabled && this.selected) this.renderBody();
1395
+ if (this.enabled && this.selected) {
1396
+ this.reapplyScope();
1397
+ this.renderBody();
1398
+ }
1383
1399
  }
1384
1400
  };
1385
1401
  this.reposition = () => {
@@ -1645,6 +1661,7 @@ var Uivisor = class {
1645
1661
  const up = () => {
1646
1662
  handle.removeEventListener("pointermove", move);
1647
1663
  handle.removeEventListener("pointerup", up);
1664
+ this.reapplyScope();
1648
1665
  this.renderBody();
1649
1666
  };
1650
1667
  handle.addEventListener("pointermove", move);
@@ -1680,6 +1697,7 @@ var Uivisor = class {
1680
1697
  dimUnit: {}
1681
1698
  });
1682
1699
  }
1700
+ if (el) this.reapplyScope();
1683
1701
  this.reposition();
1684
1702
  this.renderBody();
1685
1703
  }
@@ -1706,6 +1724,12 @@ var Uivisor = class {
1706
1724
  const el = this.selected;
1707
1725
  const st = this.st();
1708
1726
  if (!el || !st) return "";
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
+ }
1709
1733
  const inline = el.style.getPropertyValue(css);
1710
1734
  if (inline && !inline.includes("var(")) return inline;
1711
1735
  return this.computedVal(css) || st.original[css] || "";
@@ -1728,7 +1752,10 @@ var Uivisor = class {
1728
1752
  isChanged(cssList) {
1729
1753
  const st = this.st();
1730
1754
  if (!st) return false;
1731
- return cssList.some((c) => st.record.changes.some((ch) => ch.property === c));
1755
+ const scope = this.activeScope();
1756
+ return cssList.some(
1757
+ (c) => st.record.changes.some((ch) => ch.property === c && ch.breakpoint === scope.name)
1758
+ );
1732
1759
  }
1733
1760
  selectCurrent(css) {
1734
1761
  let v = this.liveVal(css).trim();
@@ -1773,16 +1800,27 @@ var Uivisor = class {
1773
1800
  }
1774
1801
  /** Re-apply recorded overrides after the target (all ↔ one) changes. */
1775
1802
  reapplyForTarget() {
1776
- const el = this.selected;
1777
- const st = this.st();
1778
- if (!el || !st) return;
1779
- const sibs = this.siblingsOf(el);
1780
- const targets = this.targetEls();
1781
- const props = new Set(st.record.changes.map((c) => c.property));
1782
- for (const css of props) {
1783
- for (const e of sibs) removeOverride(e, css);
1784
- const c = st.record.changes.find((ch) => ch.property === css);
1785
- if (c) for (const e of targets) applyOverride(e, css, c.live ?? c.after.computed);
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
+ }
1786
1824
  }
1787
1825
  this.reposition();
1788
1826
  }
@@ -1974,6 +2012,13 @@ var Uivisor = class {
1974
2012
  cache.set(el, out);
1975
2013
  return out;
1976
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
+ }
1977
2022
  /** Is any of `props` authored in the project CSS? For inherited properties we
1978
2023
  * also walk ancestors (a body/parent font rule still counts as "from the file"). */
1979
2024
  isAuthored(props, inherit) {
@@ -2014,14 +2059,9 @@ var Uivisor = class {
2014
2059
  };
2015
2060
  const px4 = (pre, suf = "") => [`${pre}-top${suf}`, `${pre}-right${suf}`, `${pre}-bottom${suf}`, `${pre}-left${suf}`];
2016
2061
  const clip = (s, n = 30) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
2017
- const changedSet = new Set(this.st()?.record.changes.map((c) => c.property) ?? []);
2018
- const stateCls = (props, inherit = false) => {
2019
- if (props.some((p) => changedSet.has(p))) return " changed";
2020
- return this.isAuthored(props, inherit) ? "" : " auto";
2021
- };
2022
2062
  const rows = [];
2023
- const add = (k, v, props, inherit = false) => {
2024
- if (v !== "" && v != null) rows.push({ k, v, cls: stateCls(props, inherit) });
2063
+ const add = (k, v, _props = [], _inherit = false) => {
2064
+ if (v !== "" && v != null) rows.push({ k, v });
2025
2065
  };
2026
2066
  const disp = g("display");
2027
2067
  add("display", disp, ["display"]);
@@ -2108,10 +2148,7 @@ var Uivisor = class {
2108
2148
  const tr = g("transform");
2109
2149
  if (tr && tr !== "none") add("transform", clip(tr, 22), ["transform"]);
2110
2150
  const collapsed = this.collapsedSecs.has("Current styles");
2111
- 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>`;
2112
- const items = collapsed ? "" : legend + rows.map(
2113
- (r) => `<div class="uiv-rrow"><span class="uiv-rk">${r.k}</span><span class="uiv-rv${r.cls}">${r.v}</span></div>`
2114
- ).join("");
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("");
2115
2152
  return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
2116
2153
  }
2117
2154
  /** Breakpoint scope switcher: shows the PROJECT's breakpoints + the live window one. */
@@ -2165,12 +2202,14 @@ var Uivisor = class {
2165
2202
  return true;
2166
2203
  }
2167
2204
  controlsHtml(ctx) {
2168
- return SECTIONS.map((sec) => {
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) => {
2169
2207
  const controls = sec.controls.filter((c) => this.relevant(c, ctx));
2170
2208
  if (!controls.length) return "";
2171
2209
  const rows = this.collapsedSecs.has(sec.title) ? "" : controls.map((c) => this.controlRow(c)).join("");
2172
2210
  return `<div class="uiv-sec">${this.accordionTitle(sec.title)}${rows}</div>`;
2173
2211
  }).join("");
2212
+ return legend + secs;
2174
2213
  }
2175
2214
  /** A collapsible section header. Clicking it hides/shows the section's controls. */
2176
2215
  accordionTitle(title) {
@@ -2245,7 +2284,7 @@ var Uivisor = class {
2245
2284
  const d = this.dimDisplay(c);
2246
2285
  const changed = this.isChanged([c.css]);
2247
2286
  const units = c.units.map((u) => `<option value="${u}"${u === d.unit ? " selected" : ""}>${UNIT_LABELS[u] ?? u}</option>`).join("");
2248
- 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>`;
2249
2288
  }
2250
2289
  /** A design-token picker row for a property, shown only when the project exposes
2251
2290
  * tokens for that category. Picking a token applies its value + tags the prompt. */
@@ -2279,7 +2318,7 @@ var Uivisor = class {
2279
2318
  const info = this.numInfo(cssList);
2280
2319
  const changed = this.isChanged(cssList);
2281
2320
  const open = this.expanded.has(c.key);
2282
- 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>`;
2283
2322
  if (open) {
2284
2323
  html += `<div class="uiv-sides">` + c.sides.map((s) => {
2285
2324
  const v = this.liveNum(s.css);
@@ -2297,7 +2336,7 @@ var Uivisor = class {
2297
2336
  }
2298
2337
  if (c.kind === "len") {
2299
2338
  const v = this.liveNum(c.css);
2300
- 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");
2301
2340
  }
2302
2341
  if (c.kind === "dim") {
2303
2342
  return this.dimField(c);
@@ -2306,10 +2345,10 @@ var Uivisor = class {
2306
2345
  const cur = this.selectCurrent(c.css);
2307
2346
  const optList = cur && !c.options.includes(cur) ? [cur, ...c.options] : c.options;
2308
2347
  const opts = optList.map((o) => `<option value="${o}"${o === cur ? " selected" : ""}>${o}</option>`).join("");
2309
- 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>`;
2310
2349
  }
2311
2350
  const val = toHexInput(this.liveVal(c.css));
2312
- 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");
2313
2352
  }
2314
2353
  bindControls() {
2315
2354
  const root = this.root;
@@ -2410,6 +2449,7 @@ var Uivisor = class {
2410
2449
  this.toggleResponsive(true);
2411
2450
  } else {
2412
2451
  this.setFrameWidth(w);
2452
+ this.reapplyScope();
2413
2453
  this.renderBody();
2414
2454
  }
2415
2455
  });
@@ -2607,13 +2647,9 @@ var Uivisor = class {
2607
2647
  for (const ent of snap.entries) {
2608
2648
  const st = { record: clone(ent.record), original: { ...ent.original }, applied: /* @__PURE__ */ new Set(), dimUnit: { ...ent.dimUnit } };
2609
2649
  this.states.set(ent.el, st);
2610
- const targets = st.record.target === "all" ? this.siblingsOf(ent.el) : [ent.el];
2611
- for (const c of st.record.changes) {
2612
- for (const e of targets) applyOverride(e, c.property, c.live ?? c.after.computed);
2613
- st.applied.add(c.property);
2614
- }
2615
2650
  }
2616
2651
  this.selected = snap.selected && this.states.has(snap.selected) ? snap.selected : null;
2652
+ this.reapplyScope();
2617
2653
  this.reposition();
2618
2654
  this.renderBody();
2619
2655
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uivisor",
3
- "version": "0.1.6",
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",