uivisor 0.1.9 → 0.1.10

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.
Files changed (2) hide show
  1. package/dist/overlay/index.js +181 -48
  2. package/package.json +1 -1
@@ -456,10 +456,10 @@ var SECTIONS = [
456
456
  {
457
457
  title: "Size",
458
458
  controls: [
459
- { kind: "len", css: "width", label: "Width", icon: ICONS.width },
460
- { kind: "len", css: "height", label: "Height", icon: ICONS.height },
461
- { kind: "len", css: "max-width", label: "Max W", icon: ICONS.width },
462
- { kind: "len", css: "min-height", label: "Min H", icon: ICONS.height }
459
+ { kind: "len", css: "width", label: "Width", icon: ICONS.width, hideWhenAuto: true },
460
+ { kind: "len", css: "height", label: "Height", icon: ICONS.height, hideWhenAuto: true },
461
+ { kind: "len", css: "max-width", label: "Max W", icon: ICONS.width, hideWhenAuto: true },
462
+ { kind: "len", css: "min-height", label: "Min H", icon: ICONS.height, hideWhenAuto: true }
463
463
  ]
464
464
  },
465
465
  {
@@ -856,9 +856,29 @@ function renderPrompt(records) {
856
856
  );
857
857
  lines.push("");
858
858
  lines.push(
859
- "These are visual tweaks I made in the running app. Apply them to the source. Do not change anything else."
859
+ "These are visual tweaks I made in the running app. Apply them to the source \u2014 but do NOT just mechanically apply them. First analyze the markup: if a change exposes dead, redundant, or contradictory code, stop and tell me (or propose the cleanup) instead of blindly applying."
860
860
  );
861
861
  lines.push("");
862
+ lines.push("## Analyze the markup first (don\u2019t just change values)");
863
+ lines.push(
864
+ "- If a change effectively HIDES or empties an element (display:none, visibility:hidden, opacity:0, or collapsing its size to 0) and it isn\u2019t shown conditionally somewhere else, question whether the element/component is needed at all. Prefer DELETING the element and its now-unused styles/classes over leaving a permanently hidden node."
865
+ );
866
+ lines.push(
867
+ "- Flag CSS that has no effect in its context and ask why it\u2019s there \u2014 don\u2019t silently keep it:"
868
+ );
869
+ lines.push(
870
+ " \u2022 flex-direction / justify-content / align-items / flex-wrap / gap on an element whose display is NOT flex/grid (e.g. block);"
871
+ );
872
+ lines.push(" \u2022 flex-grow / flex-shrink / flex-basis / align-self / order when the PARENT isn\u2019t flex/grid;");
873
+ lines.push(" \u2022 top / right / bottom / left / z-index when position is static;");
874
+ lines.push(
875
+ " \u2022 width / height on an inline element; a value identical to the inherited/default (a no-op)."
876
+ );
877
+ lines.push(
878
+ "- If a tweak only makes sense because of an underlying layout problem, fix the cause, don\u2019t patch the symptom. Remove dead code rather than piling on more utilities/overrides."
879
+ );
880
+ lines.push("- Per-element notes flagged below (\u26A0) are concrete instances I already detected \u2014 address them.");
881
+ lines.push("");
862
882
  let anyClassTarget = false;
863
883
  let anyNewClass = false;
864
884
  let anyDesignToken = false;
@@ -927,6 +947,10 @@ function renderPrompt(records) {
927
947
  );
928
948
  }
929
949
  }
950
+ if (r.smells?.length) {
951
+ lines.push(`- \u26A0 Analyze this element (don\u2019t just apply \u2014 these look off):`);
952
+ for (const s of r.smells) lines.push(` - ${s}`);
953
+ }
930
954
  lines.push("");
931
955
  });
932
956
  lines.push("### Rules");
@@ -1243,10 +1267,22 @@ var CSS = (
1243
1267
  .uiv-rv.changed { color: #4ade80; } /* edited in uivisor \u2192 green */
1244
1268
 
1245
1269
  /* control-row state: file (authored) \xB7 edited (this breakpoint) \xB7 auto (computed) */
1246
- .uiv-ctl.st-file > .clabel { color: #e4e4e7; }
1247
- .uiv-ctl.st-edited > .clabel { color: #4ade80; }
1248
- .uiv-ctl.st-auto > .clabel { color: #6b7280; }
1249
- .uiv-ctl.st-inherit > .clabel { color: #38bdf8; } /* value cascaded from another bp */
1270
+ /* 3-state colour on BOTH the label and the value (input / select text) */
1271
+ .uiv-ctl.st-file > .clabel,
1272
+ .uiv-ctl.st-file .uiv-num input, .uiv-ctl.st-file select.uiv-sel { color: #e4e4e7; }
1273
+ .uiv-ctl.st-edited > .clabel,
1274
+ .uiv-ctl.st-edited .uiv-num input, .uiv-ctl.st-edited select.uiv-sel { color: #4ade80; }
1275
+ .uiv-ctl.st-inherit > .clabel,
1276
+ .uiv-ctl.st-inherit .uiv-num input, .uiv-ctl.st-inherit select.uiv-sel { color: #38bdf8; } /* cascaded */
1277
+ .uiv-ctl.st-auto > .clabel,
1278
+ .uiv-ctl.st-auto .uiv-num input, .uiv-ctl.st-auto select.uiv-sel { color: #6b7280; }
1279
+
1280
+ /* "+" chips for hidden auto controls (width/height when not set) */
1281
+ .uiv-adds { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
1282
+ .uiv-addctl { cursor: pointer; border: 1px dashed #52525b; background: transparent;
1283
+ color: #71717a; border-radius: 6px; padding: 3px 8px; font-size: 11px; font-weight: 600;
1284
+ font-family: ui-monospace, monospace; }
1285
+ .uiv-addctl:hover { color: #fff; border-color: #6366f1; }
1250
1286
  .uiv-inh { font-size: 9px; font-weight: 700; color: #38bdf8; font-family: ui-monospace, monospace;
1251
1287
  background: #0c4a6e55; border: 1px solid #0369a1; border-radius: 4px; padding: 0 3px; margin-left: 2px; }
1252
1288
  .uiv-leg { display: flex; gap: 12px; padding: 8px 12px 2px; font-size: 9px;
@@ -1346,6 +1382,16 @@ var CSS = (
1346
1382
  .uiv-btn.primary { background: #4f46e5; border-color: #6366f1; color: #fff; flex-basis: 100%; }
1347
1383
  .uiv-btn.primary:hover { background: #4338ca; }
1348
1384
  .uiv-btn.ghost { flex: 0 0 auto; }
1385
+ /* floating read-only "all styles" block, docked bottom-right, left of the panel */
1386
+ .uiv-info { position: fixed; right: 352px; bottom: 16px; z-index: 2147483646;
1387
+ width: 216px; max-height: 52vh; overflow: auto; display: none;
1388
+ background: rgba(24,24,27,0.86); color: #e4e4e7;
1389
+ border: 1px solid #3f3f46; border-radius: 10px; padding: 8px 10px;
1390
+ font-size: 11px; box-shadow: 0 8px 28px rgba(0,0,0,0.4); }
1391
+ .uiv-info.show { display: block; }
1392
+ .uiv-info-h { font-size: 10px; text-transform: uppercase; letter-spacing: .4px;
1393
+ color: #8b8b94; font-weight: 600; margin-bottom: 6px; }
1394
+ .uiv-info-sub { color: #52525b; }
1349
1395
  .uiv-toast { position: fixed; right: 16px; bottom: 128px; z-index: 2147483647;
1350
1396
  background: #22c55e; color: #052e16; padding: 8px 12px; border-radius: 8px;
1351
1397
  font-size: 12px; font-weight: 600; display: none; }
@@ -1374,6 +1420,8 @@ var Uivisor = class {
1374
1420
  this.expanded = /* @__PURE__ */ new Set();
1375
1421
  /** Section titles collapsed in the accordion (per session). */
1376
1422
  this.collapsedSecs = /* @__PURE__ */ new Set();
1423
+ /** Controls manually revealed via "+" (hideWhenAuto controls that are auto). */
1424
+ this.revealedCtls = /* @__PURE__ */ new Set();
1377
1425
  /** Undo / redo stacks of full edit-state snapshots. */
1378
1426
  this.undoStack = [];
1379
1427
  this.redoStack = [];
@@ -1512,7 +1560,7 @@ var Uivisor = class {
1512
1560
  this.root.innerHTML = `
1513
1561
  <style>${CSS}</style>
1514
1562
  <div class="uiv-framewrap">
1515
- <div class="uiv-framebar"><div class="uiv-framechips uiv-chips"></div><span class="uiv-framew">768px</span><span class="uiv-framex" title="Exit responsive">\u2715</span></div>
1563
+ <div class="uiv-framebar"><div class="uiv-framechips uiv-chips"></div><span class="uiv-framew">768px</span><span class="uiv-framex" title="Turn uivisor off (Alt+U)">\u2715</span></div>
1516
1564
  <div class="uiv-framestage">
1517
1565
  <div class="uiv-framehost">
1518
1566
  <iframe class="uiv-frame" data-uiv-frame="1"></iframe>
@@ -1524,6 +1572,7 @@ var Uivisor = class {
1524
1572
  <div class="uiv-box sel"></div>
1525
1573
  <div class="uiv-tag"></div>
1526
1574
  <div class="uiv-fab" title="Toggle uivisor (Alt+U)">\u25CE</div>
1575
+ <div class="uiv-info"></div>
1527
1576
  <div class="uiv-toast"></div>
1528
1577
  <div class="uiv-panel">
1529
1578
  <div class="uiv-head">
@@ -1551,7 +1600,7 @@ var Uivisor = class {
1551
1600
  this.frame = this.q(".uiv-frame");
1552
1601
  this.fab.addEventListener("click", () => this.toggle());
1553
1602
  this.q(".uiv-x").addEventListener("click", () => this.toggle(false));
1554
- this.q(".uiv-framex").addEventListener("click", () => this.toggleResponsive(false));
1603
+ this.q(".uiv-framex").addEventListener("click", () => this.toggle(false));
1555
1604
  this.q(".copy-prompt").addEventListener("click", () => this.copyPrompt());
1556
1605
  this.q(".copy-json").addEventListener("click", () => this.copyJSON());
1557
1606
  this.q(".reset").addEventListener("click", () => this.resetSelected());
@@ -1597,11 +1646,26 @@ var Uivisor = class {
1597
1646
  if (!this.enabled) {
1598
1647
  this.hoverBox.style.display = "none";
1599
1648
  this.tag.style.display = "none";
1649
+ this.renderInfo();
1600
1650
  if (this.responsive) this.toggleResponsive(false);
1601
1651
  } else {
1602
1652
  this.scheduleBpRefresh();
1653
+ if (!this.responsive) {
1654
+ this.frameWidth = this.defaultFrameWidth();
1655
+ this.toggleResponsive(true);
1656
+ }
1603
1657
  }
1604
1658
  }
1659
+ /** Frame width on enable: the real window width (≈ the current breakpoint). */
1660
+ defaultFrameWidth() {
1661
+ return typeof window !== "undefined" ? window.innerWidth : 1280;
1662
+ }
1663
+ /** Frame width for the "all"/base chip: a phone-ish width in the base range. */
1664
+ baseFrameWidth() {
1665
+ const bps = this.bpSystem().breakpoints;
1666
+ const firstBp = bps.length ? bps[0].minWidth : 640;
1667
+ return this.bpSystem().dir === "min" ? Math.min(390, firstBp - 1) : 390;
1668
+ }
1605
1669
  /** Stylesheets (esp. JIT/CDN Tailwind) load async — re-detect breakpoints a few
1606
1670
  * times after enabling and re-render only if the set actually changed. */
1607
1671
  scheduleBpRefresh() {
@@ -2026,6 +2090,7 @@ var Uivisor = class {
2026
2090
  ${this.journalHtml()}
2027
2091
  `;
2028
2092
  if (this.responsive) this.renderFrameBar();
2093
+ this.renderInfo();
2029
2094
  this.bindControls();
2030
2095
  return;
2031
2096
  }
@@ -2039,13 +2104,13 @@ var Uivisor = class {
2039
2104
  <span class="uiv-mech">${st.record.styling.primaryMechanism}</span>
2040
2105
  </div>
2041
2106
  ${this.dsIndicatorHtml()}
2042
- ${this.currentStylesHtml()}
2043
2107
  ${this.breakpointBarHtml()}
2044
2108
  ${this.targetHtml(st)}
2045
2109
  ${this.controlsHtml(this.context(this.selected))}
2046
2110
  ${this.journalHtml()}
2047
2111
  `;
2048
2112
  if (this.responsive) this.renderFrameBar();
2113
+ this.renderInfo();
2049
2114
  this.bindControls();
2050
2115
  }
2051
2116
  /** Small indicator: how many design tokens were detected (or a hint if none). */
@@ -2104,14 +2169,17 @@ var Uivisor = class {
2104
2169
  cache.set(el, out);
2105
2170
  return out;
2106
2171
  }
2107
- /** State class for an editable control's row: edited (green, at this breakpoint)
2108
- * · inherit (a value cascaded from another breakpoint) · file (white, authored
2109
- * in CSS) · auto (grey, browser-computed/default). */
2110
- controlStateClass(props) {
2111
- if (this.isChanged(props)) return " st-edited";
2112
- if (this.inheritedFrom(props)) return " st-inherit";
2172
+ /** Editing state of a property set: edited (set at this breakpoint) · inherit
2173
+ * (cascaded from another breakpoint) · file (authored in CSS) · auto
2174
+ * (browser-computed / default, not set anywhere). */
2175
+ controlState(props) {
2176
+ if (this.isChanged(props)) return "edited";
2177
+ if (this.inheritedFrom(props)) return "inherit";
2113
2178
  const inherit = props.some((p) => INHERITED_PROPS.has(p));
2114
- return this.isAuthored(props, inherit) ? " st-file" : " st-auto";
2179
+ return this.isAuthored(props, inherit) ? "file" : "auto";
2180
+ }
2181
+ controlStateClass(props) {
2182
+ return ` st-${this.controlState(props)}`;
2115
2183
  }
2116
2184
  /** A control's label, with an "inherited from {bp}" badge when the value cascaded. */
2117
2185
  ctlLabel(label, props) {
@@ -2131,12 +2199,11 @@ var Uivisor = class {
2131
2199
  return false;
2132
2200
  }
2133
2201
  /**
2134
- * Read-only readout of the element's ACTUAL current styles, three-state coloured:
2135
- * white = authored in the project CSS · green = edited in uivisor ·
2136
- * grey = browser-computed/auto (e.g. a width a flex item derived, a default).
2137
- * Comprehensive + context-aware (flex/grid/position rows only when relevant).
2202
+ * Comprehensive read-only readout of the element's ACTUAL current styles
2203
+ * (context-aware: flex/grid/position rows only when relevant). Rendered into the
2204
+ * floating info block at the corner, not the panel. Returns the `<div.uiv-rrow>` rows.
2138
2205
  */
2139
- currentStylesHtml() {
2206
+ styleRows() {
2140
2207
  const el = this.selected;
2141
2208
  if (!el) return "";
2142
2209
  let cs;
@@ -2247,9 +2314,19 @@ var Uivisor = class {
2247
2314
  if (z && z !== "auto") add("z-index", z, ["z-index"]);
2248
2315
  const tr = g("transform");
2249
2316
  if (tr && tr !== "none") add("transform", clip(tr, 22), ["transform"]);
2250
- const collapsed = this.collapsedSecs.has("Current styles");
2251
- 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("");
2252
- return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
2317
+ return rows.map((r) => `<div class="uiv-rrow"><span class="uiv-rk">${r.k}</span><span class="uiv-rv">${r.v}</span></div>`).join("");
2318
+ }
2319
+ /** Floating read-only "all styles" block, docked at the bottom-right corner so it
2320
+ * doesn't take space in the panel. Shown only while an element is selected. */
2321
+ renderInfo() {
2322
+ const info = this.q(".uiv-info");
2323
+ if (!this.enabled || !this.selected) {
2324
+ info.classList.remove("show");
2325
+ info.innerHTML = "";
2326
+ return;
2327
+ }
2328
+ info.innerHTML = `<div class="uiv-info-h">all styles <span class="uiv-info-sub">computed</span></div><div class="uiv-readout">${this.styleRows()}</div>`;
2329
+ info.classList.add("show");
2253
2330
  }
2254
2331
  /** Display label for a breakpoint name — the unprefixed "base" scope reads "all"
2255
2332
  * (applies to every size by default); internal key stays "base". */
@@ -2272,11 +2349,10 @@ var Uivisor = class {
2272
2349
  const frameBp = this.responsive ? activeBreakpoint(this.frameWidth, sys).name : null;
2273
2350
  const winBp = currentBreakpoint(sys).name;
2274
2351
  const isActive = (n) => this.responsive ? n === frameBp : n === winBp;
2275
- const chip = (n, on, title) => `<button class="uiv-chip${on ? " on" : ""}" data-bp="${n}" title="${escapeAttr(title)}">${this.bpIcon(n)}<span>${n === "live" ? "Live" : this.bpLabel(n)}</span></button>`;
2352
+ const chip = (n, on, title) => `<button class="uiv-chip${on ? " on" : ""}" data-bp="${n}" title="${escapeAttr(title)}">${this.bpIcon(n)}<span>${this.bpLabel(n)}</span></button>`;
2276
2353
  const all = chip("base", isActive("base"), "No breakpoint \u2014 applies to every size by default");
2277
- const live = chip("live", !this.responsive, "Follow your real browser window");
2278
2354
  const rest = sys.breakpoints.map((b) => chip(b.name, isActive(b.name), sys.dir === "min" ? `\u2265 ${b.minWidth}px` : `\u2264 ${b.minWidth}px`)).join("");
2279
- return all + live + rest;
2355
+ return all + rest;
2280
2356
  }
2281
2357
  /** Panel breakpoint bar — shown only in Live mode (in responsive mode the bar
2282
2358
  * lives over the virtual screen instead). */
@@ -2332,8 +2408,19 @@ var Uivisor = class {
2332
2408
  const secs = SECTIONS.map((sec) => {
2333
2409
  const controls = sec.controls.filter((c) => this.relevant(c, ctx));
2334
2410
  if (!controls.length) return "";
2335
- const rows = this.collapsedSecs.has(sec.title) ? "" : controls.map((c) => this.controlRow(c)).join("");
2336
- return `<div class="uiv-sec">${this.accordionTitle(sec.title)}${rows}</div>`;
2411
+ if (this.collapsedSecs.has(sec.title)) return `<div class="uiv-sec">${this.accordionTitle(sec.title)}</div>`;
2412
+ const rows = [];
2413
+ const adds = [];
2414
+ for (const c of controls) {
2415
+ const css = c.css;
2416
+ if (c.kind === "len" && c.hideWhenAuto && css && !this.revealedCtls.has(css) && this.controlState([css]) === "auto") {
2417
+ adds.push(`<button class="uiv-addctl" data-css="${css}">+ ${escapeHtml(c.label)}</button>`);
2418
+ } else {
2419
+ rows.push(this.controlRow(c));
2420
+ }
2421
+ }
2422
+ const addRow = adds.length ? `<div class="uiv-adds">${adds.join("")}</div>` : "";
2423
+ return `<div class="uiv-sec">${this.accordionTitle(sec.title)}${rows.join("")}${addRow}</div>`;
2337
2424
  }).join("");
2338
2425
  return legend + secs;
2339
2426
  }
@@ -2462,7 +2549,10 @@ var Uivisor = class {
2462
2549
  }
2463
2550
  if (c.kind === "len") {
2464
2551
  const v = this.liveNum(c.css);
2465
- return `<div class="uiv-ctl${this.controlStateClass([c.css])}">${this.ctlLabel(c.label, [c.css])}<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");
2552
+ const auto = c.hideWhenAuto && this.controlState([c.css]) === "auto";
2553
+ const value = auto || v == null ? "" : String(round2(v));
2554
+ const placeholder = auto && v != null ? `${round2(v)} (auto)` : "\u2014";
2555
+ return `<div class="uiv-ctl${this.controlStateClass([c.css])}">${this.ctlLabel(c.label, [c.css])}<div class="cfield">${this.numField(c.css, value, c.icon, this.isChanged([c.css]), false, placeholder)}</div><span></span></div>` + this.tokenRowHtml(c.css, "Token");
2466
2556
  }
2467
2557
  if (c.kind === "dim") {
2468
2558
  return this.dimField(c);
@@ -2560,24 +2650,22 @@ var Uivisor = class {
2560
2650
  this.renderBody();
2561
2651
  });
2562
2652
  });
2653
+ root.querySelectorAll(".uiv-addctl").forEach((node) => {
2654
+ const btn = node;
2655
+ const css = btn.getAttribute("data-css");
2656
+ btn.addEventListener("click", () => {
2657
+ this.revealedCtls.add(css);
2658
+ this.renderBody();
2659
+ });
2660
+ });
2563
2661
  root.querySelectorAll(".uiv-chip").forEach((node) => {
2564
2662
  const btn = node;
2565
2663
  const bp = btn.getAttribute("data-bp");
2566
2664
  btn.addEventListener("click", () => {
2567
- if (bp === "live") {
2568
- if (this.responsive) this.toggleResponsive(false);
2569
- else this.renderBody();
2570
- return;
2571
- }
2572
- const w = bp === "base" ? 390 : this.bpSystem().breakpoints.find((b) => b.name === bp)?.minWidth ?? 768;
2573
- this.frameWidth = w;
2574
- if (!this.responsive) {
2575
- this.toggleResponsive(true);
2576
- } else {
2577
- this.setFrameWidth(w);
2578
- this.reapplyScope();
2579
- this.renderBody();
2580
- }
2665
+ const w = bp === "base" ? this.baseFrameWidth() : this.bpSystem().breakpoints.find((b) => b.name === bp)?.minWidth ?? 768;
2666
+ this.setFrameWidth(w);
2667
+ this.reapplyScope();
2668
+ this.renderBody();
2581
2669
  });
2582
2670
  });
2583
2671
  root.querySelectorAll(".uiv-clschip").forEach((node) => {
@@ -2697,7 +2785,52 @@ var Uivisor = class {
2697
2785
  }
2698
2786
  // ---- actions ----
2699
2787
  records() {
2700
- return [...this.states.values()].map((s) => s.record).filter((r) => r.changes.length > 0);
2788
+ return [...this.states.entries()].filter(([, s]) => s.record.changes.length > 0).map(([el, s]) => ({ ...s.record, smells: this.detectSmells(el) }));
2789
+ }
2790
+ /**
2791
+ * Detect dead / redundant / contradictory CSS on an element so the prompt can ask
2792
+ * the agent to QUESTION it instead of blindly applying — e.g. flex-direction on a
2793
+ * non-flex element, offsets without positioning, a display:none node that may be
2794
+ * deletable. Only flags properties the project actually AUTHORED (matchedProps).
2795
+ */
2796
+ detectSmells(el) {
2797
+ const out = [];
2798
+ try {
2799
+ const cs = getComputedStyle(el);
2800
+ const authored = this.matchedProps(el);
2801
+ const disp = cs.display;
2802
+ const flexGrid = /flex|grid/.test(disp);
2803
+ const parent = el.parentElement;
2804
+ const parentFlexGrid = parent ? /flex|grid/.test(getComputedStyle(parent).display) : false;
2805
+ const has = (p) => authored.has(p);
2806
+ if (disp === "none")
2807
+ out.push(
2808
+ `It computes to \`display:none\` \u2014 it renders nothing here. If it isn't toggled visible elsewhere, prefer DELETING the element and its now-dead styles/classes over shipping a permanently hidden node.`
2809
+ );
2810
+ if (cs.visibility === "hidden" || parseFloat(cs.opacity) === 0)
2811
+ out.push(`It's invisible (visibility/opacity 0) \u2014 question whether it's needed at all.`);
2812
+ if (!flexGrid) {
2813
+ for (const p of ["flex-direction", "justify-content", "align-items", "flex-wrap"])
2814
+ if (has(p))
2815
+ out.push(`\`${p}\` is set but \`display\` is \`${disp}\` (not flex/grid) \u2192 it has NO effect. Remove it, or the display is wrong \u2014 ask which.`);
2816
+ }
2817
+ if (!parentFlexGrid) {
2818
+ for (const p of ["flex-grow", "flex-shrink", "flex-basis", "align-self", "order"])
2819
+ if (has(p))
2820
+ out.push(`\`${p}\` is set but the PARENT isn't flex/grid \u2192 no effect. Remove it or fix the parent.`);
2821
+ }
2822
+ if (cs.position === "static") {
2823
+ for (const p of ["top", "right", "bottom", "left", "z-index"])
2824
+ if (has(p))
2825
+ out.push(`\`${p}\` is set but \`position\` is \`static\` \u2192 no effect. Add positioning or drop it.`);
2826
+ }
2827
+ if (disp === "inline") {
2828
+ for (const p of ["width", "height"])
2829
+ if (has(p)) out.push(`\`${p}\` on an \`inline\` element has no effect (needs inline-block/block).`);
2830
+ }
2831
+ } catch {
2832
+ }
2833
+ return [...new Set(out)].slice(0, 6);
2701
2834
  }
2702
2835
  async copyPrompt() {
2703
2836
  const recs = this.records();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uivisor",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
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",