uivisor 0.1.7 → 0.1.8
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 +160 -38
- package/package.json +1 -1
package/dist/overlay/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/overlay/breakpoint.ts
|
|
2
2
|
var TAILWIND = {
|
|
3
3
|
name: "tailwind",
|
|
4
|
+
dir: "min",
|
|
4
5
|
breakpoints: [
|
|
5
6
|
{ name: "sm", minWidth: 640 },
|
|
6
7
|
{ name: "md", minWidth: 768 },
|
|
@@ -9,13 +10,44 @@ var TAILWIND = {
|
|
|
9
10
|
{ name: "2xl", minWidth: 1536 }
|
|
10
11
|
]
|
|
11
12
|
};
|
|
13
|
+
function appliesAt(dir, threshold, width) {
|
|
14
|
+
return dir === "min" ? width >= threshold : width <= threshold;
|
|
15
|
+
}
|
|
16
|
+
function priority(dir, threshold) {
|
|
17
|
+
return dir === "min" ? threshold : -threshold;
|
|
18
|
+
}
|
|
19
|
+
function baseThreshold(dir) {
|
|
20
|
+
return dir === "min" ? 0 : Infinity;
|
|
21
|
+
}
|
|
12
22
|
function activeBreakpoint(width, system = TAILWIND) {
|
|
13
|
-
let active = { name: "base", minWidth:
|
|
23
|
+
let active = { name: "base", minWidth: baseThreshold(system.dir) };
|
|
24
|
+
let best = priority(system.dir, active.minWidth);
|
|
14
25
|
for (const bp of system.breakpoints) {
|
|
15
|
-
if (
|
|
26
|
+
if (!appliesAt(system.dir, bp.minWidth, width)) continue;
|
|
27
|
+
const p = priority(system.dir, bp.minWidth);
|
|
28
|
+
if (p > best) {
|
|
29
|
+
best = p;
|
|
30
|
+
active = { name: bp.name, minWidth: bp.minWidth };
|
|
31
|
+
}
|
|
16
32
|
}
|
|
17
33
|
return active;
|
|
18
34
|
}
|
|
35
|
+
function effectiveBreakpoint(editedNames, width, system = TAILWIND) {
|
|
36
|
+
const stops = { base: baseThreshold(system.dir) };
|
|
37
|
+
for (const bp of system.breakpoints) stops[bp.name] = bp.minWidth;
|
|
38
|
+
let winner = null;
|
|
39
|
+
let best = -Infinity;
|
|
40
|
+
for (const name of editedNames) {
|
|
41
|
+
const threshold = stops[name];
|
|
42
|
+
if (threshold == null || !appliesAt(system.dir, threshold, width)) continue;
|
|
43
|
+
const p = priority(system.dir, threshold);
|
|
44
|
+
if (winner === null || p > best) {
|
|
45
|
+
best = p;
|
|
46
|
+
winner = name;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return winner;
|
|
50
|
+
}
|
|
19
51
|
function currentBreakpoint(system = TAILWIND) {
|
|
20
52
|
const width = typeof window !== "undefined" ? window.innerWidth : 0;
|
|
21
53
|
return activeBreakpoint(width, system);
|
|
@@ -32,18 +64,23 @@ function nameForWidth(px2) {
|
|
|
32
64
|
}
|
|
33
65
|
function detectBreakpoints() {
|
|
34
66
|
if (typeof document === "undefined") return TAILWIND;
|
|
35
|
-
const
|
|
67
|
+
const mins = /* @__PURE__ */ new Set();
|
|
68
|
+
const maxes = /* @__PURE__ */ new Set();
|
|
69
|
+
const grab = (text, re, into) => {
|
|
70
|
+
const m = re.exec(text);
|
|
71
|
+
if (m) {
|
|
72
|
+
const val = parseFloat(m[1]);
|
|
73
|
+
const px2 = m[2].toLowerCase() === "px" ? val : val * 16;
|
|
74
|
+
if (px2 >= 240 && px2 <= 4096) into.add(Math.round(px2));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
36
77
|
const visit = (rules) => {
|
|
37
78
|
if (!rules) return;
|
|
38
79
|
for (let i = 0; i < rules.length; i++) {
|
|
39
80
|
const rule = rules[i];
|
|
40
81
|
if (rule.type === 4 && rule.media) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const val = parseFloat(m[1]);
|
|
44
|
-
const px2 = m[2].toLowerCase() === "px" ? val : val * 16;
|
|
45
|
-
if (px2 >= 240 && px2 <= 4096) widths.add(Math.round(px2));
|
|
46
|
-
}
|
|
82
|
+
grab(rule.media.mediaText, /min-width:\s*(\d*\.?\d+)(px|rem|em)/i, mins);
|
|
83
|
+
grab(rule.media.mediaText, /max-width:\s*(\d*\.?\d+)(px|rem|em)/i, maxes);
|
|
47
84
|
visit(rule.cssRules);
|
|
48
85
|
} else if (rule.cssRules) {
|
|
49
86
|
visit(rule.cssRules);
|
|
@@ -56,10 +93,13 @@ function detectBreakpoints() {
|
|
|
56
93
|
} catch {
|
|
57
94
|
}
|
|
58
95
|
}
|
|
96
|
+
const useMax = maxes.size > mins.size;
|
|
97
|
+
const widths = useMax ? maxes : mins;
|
|
59
98
|
const sorted = [...widths].sort((a, b) => a - b);
|
|
60
99
|
if (!sorted.length) return TAILWIND;
|
|
61
100
|
return {
|
|
62
101
|
name: "detected",
|
|
102
|
+
dir: useMax ? "max" : "min",
|
|
63
103
|
breakpoints: sorted.map((w) => ({ name: nameForWidth(w), minWidth: w }))
|
|
64
104
|
};
|
|
65
105
|
}
|
|
@@ -366,7 +406,11 @@ var ICONS = {
|
|
|
366
406
|
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
407
|
width: sv('<path d="M1.5 8 H14.5 M4 5 L1.5 8 L4 11 M12 5 L14.5 8 L12 11"/>'),
|
|
368
408
|
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"/>')
|
|
409
|
+
chevron: sv('<path d="M6 4 L10 8 L6 12"/>'),
|
|
410
|
+
phone: sv('<rect x="5" y="1.8" width="6" height="12.4" rx="1.6"/><path d="M7 12.4h2"/>'),
|
|
411
|
+
tablet: sv('<rect x="3.3" y="2.2" width="9.4" height="11.6" rx="1.6"/><path d="M7 11.7h2"/>'),
|
|
412
|
+
desktop: sv('<rect x="1.8" y="2.8" width="12.4" height="8" rx="1"/><path d="M5.8 14h4.4 M8 10.8v3.2"/>'),
|
|
413
|
+
live: sv('<circle cx="8" cy="8" r="2"/><path d="M4.6 4.6a4.8 4.8 0 0 0 0 6.8 M11.4 4.6a4.8 4.8 0 0 1 0 6.8"/>')
|
|
370
414
|
};
|
|
371
415
|
var SECTIONS = [
|
|
372
416
|
{
|
|
@@ -1082,12 +1126,14 @@ var CSS = (
|
|
|
1082
1126
|
}
|
|
1083
1127
|
.uiv-framewrap.show { display: flex; }
|
|
1084
1128
|
.uiv-framebar {
|
|
1085
|
-
display: flex; align-items: center;
|
|
1086
|
-
height:
|
|
1129
|
+
display: flex; align-items: center; gap: 12px;
|
|
1130
|
+
height: 46px; padding: 0 14px; color: #e4e4e7; font-size: 12px; flex: 0 0 auto;
|
|
1087
1131
|
border-bottom: 1px solid #27272a;
|
|
1088
1132
|
}
|
|
1089
|
-
.uiv-
|
|
1090
|
-
.uiv-
|
|
1133
|
+
.uiv-framechips { display: flex; gap: 6px; flex: 1; justify-content: center; flex-wrap: wrap; }
|
|
1134
|
+
.uiv-framew { font-family: ui-monospace, monospace; color: #c7d2fe; font-weight: 600;
|
|
1135
|
+
white-space: nowrap; flex: 0 0 auto; }
|
|
1136
|
+
.uiv-framex { cursor: pointer; color: #a1a1aa; font-size: 14px; flex: 0 0 auto; }
|
|
1091
1137
|
.uiv-framex:hover { color: #fff; }
|
|
1092
1138
|
.uiv-framestage {
|
|
1093
1139
|
flex: 1; display: flex; align-items: stretch; justify-content: center;
|
|
@@ -1151,7 +1197,10 @@ var CSS = (
|
|
|
1151
1197
|
border-radius: 6px; padding: 3px 8px; font-size: 11px; font-weight: 600;
|
|
1152
1198
|
font-family: ui-monospace, monospace;
|
|
1153
1199
|
}
|
|
1200
|
+
.uiv-chip { display: inline-flex; align-items: center; gap: 4px; }
|
|
1201
|
+
.uiv-chip svg { width: 13px; height: 13px; opacity: .85; }
|
|
1154
1202
|
.uiv-chip:hover, .uiv-clschip:hover { color: #fff; background: #3f3f46; }
|
|
1203
|
+
.uiv-chip.on svg { opacity: 1; }
|
|
1155
1204
|
.uiv-chip.win { border-color: #52525b; color: #d4d4d8; }
|
|
1156
1205
|
.uiv-chip.on, .uiv-clschip.on { background: #4f46e5; border-color: #6366f1; color: #fff; }
|
|
1157
1206
|
.uiv-bphint { margin-top: 7px; font-size: 10px; color: #71717a; line-height: 1.4; }
|
|
@@ -1196,11 +1245,15 @@ var CSS = (
|
|
|
1196
1245
|
.uiv-ctl.st-file > .clabel { color: #e4e4e7; }
|
|
1197
1246
|
.uiv-ctl.st-edited > .clabel { color: #4ade80; }
|
|
1198
1247
|
.uiv-ctl.st-auto > .clabel { color: #6b7280; }
|
|
1248
|
+
.uiv-ctl.st-inherit > .clabel { color: #38bdf8; } /* value cascaded from another bp */
|
|
1249
|
+
.uiv-inh { font-size: 9px; font-weight: 700; color: #38bdf8; font-family: ui-monospace, monospace;
|
|
1250
|
+
background: #0c4a6e55; border: 1px solid #0369a1; border-radius: 4px; padding: 0 3px; margin-left: 2px; }
|
|
1199
1251
|
.uiv-leg { display: flex; gap: 12px; padding: 8px 12px 2px; font-size: 9px;
|
|
1200
1252
|
text-transform: uppercase; letter-spacing: .4px; }
|
|
1201
1253
|
.uiv-lg { color: #e4e4e7; display: flex; align-items: center; gap: 4px; } /* file = white */
|
|
1202
1254
|
.uiv-lg::before { content: ''; width: 7px; height: 7px; border-radius: 2px; background: currentColor; }
|
|
1203
1255
|
.uiv-lg.edit { color: #4ade80; }
|
|
1256
|
+
.uiv-lg.inh { color: #38bdf8; }
|
|
1204
1257
|
.uiv-lg.auto { color: #6b7280; }
|
|
1205
1258
|
.uiv-sw { display: inline-block; width: 11px; height: 11px; border-radius: 3px;
|
|
1206
1259
|
border: 1px solid rgba(255,255,255,0.2); flex: 0 0 auto; }
|
|
@@ -1458,7 +1511,7 @@ var Uivisor = class {
|
|
|
1458
1511
|
this.root.innerHTML = `
|
|
1459
1512
|
<style>${CSS}</style>
|
|
1460
1513
|
<div class="uiv-framewrap">
|
|
1461
|
-
<div class="uiv-framebar"><span class="uiv-framew">768px</span><span class="uiv-framex" title="Exit responsive">\u2715
|
|
1514
|
+
<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>
|
|
1462
1515
|
<div class="uiv-framestage">
|
|
1463
1516
|
<div class="uiv-framehost">
|
|
1464
1517
|
<iframe class="uiv-frame" data-uiv-frame="1"></iframe>
|
|
@@ -1724,8 +1777,7 @@ var Uivisor = class {
|
|
|
1724
1777
|
const el = this.selected;
|
|
1725
1778
|
const st = this.st();
|
|
1726
1779
|
if (!el || !st) return "";
|
|
1727
|
-
const
|
|
1728
|
-
const ch = st.record.changes.find((c) => c.property === css && c.breakpoint === scope.name);
|
|
1780
|
+
const ch = this.effectiveChange(css);
|
|
1729
1781
|
if (ch) {
|
|
1730
1782
|
const v = ch.live ?? ch.after.computed;
|
|
1731
1783
|
return v.includes("var(") ? this.computedVal(css) || v : v;
|
|
@@ -1810,16 +1862,25 @@ var Uivisor = class {
|
|
|
1810
1862
|
* inline override leaking across all of them.
|
|
1811
1863
|
*/
|
|
1812
1864
|
reapplyScope() {
|
|
1813
|
-
const
|
|
1865
|
+
const width = this.activeWidth();
|
|
1866
|
+
const sys = this.bpSystem();
|
|
1814
1867
|
for (const [el, st] of this.states) {
|
|
1815
1868
|
const sibs = this.siblingsOf(el);
|
|
1816
1869
|
const targets = st.record.target === "all" ? sibs : [el];
|
|
1817
1870
|
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
1818
1871
|
st.applied = /* @__PURE__ */ new Set();
|
|
1872
|
+
const byProp = /* @__PURE__ */ new Map();
|
|
1819
1873
|
for (const c of st.record.changes) {
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1874
|
+
const arr = byProp.get(c.property) ?? [];
|
|
1875
|
+
arr.push(c);
|
|
1876
|
+
byProp.set(c.property, arr);
|
|
1877
|
+
}
|
|
1878
|
+
for (const [prop, changes] of byProp) {
|
|
1879
|
+
const eff = effectiveBreakpoint(changes.map((c2) => c2.breakpoint), width, sys);
|
|
1880
|
+
if (!eff) continue;
|
|
1881
|
+
const c = changes.find((x) => x.breakpoint === eff);
|
|
1882
|
+
for (const e of targets) applyOverride(e, prop, c.live ?? c.after.computed);
|
|
1883
|
+
st.applied.add(prop);
|
|
1823
1884
|
}
|
|
1824
1885
|
}
|
|
1825
1886
|
this.reposition();
|
|
@@ -1832,6 +1893,34 @@ var Uivisor = class {
|
|
|
1832
1893
|
if (this.responsive) return activeBreakpoint(this.frameWidth, sys);
|
|
1833
1894
|
return currentBreakpoint(sys);
|
|
1834
1895
|
}
|
|
1896
|
+
/** The width the inspector is scoped to (virtual screen, else real window). */
|
|
1897
|
+
activeWidth() {
|
|
1898
|
+
if (this.responsive) return this.frameWidth;
|
|
1899
|
+
return typeof window !== "undefined" ? window.innerWidth : 0;
|
|
1900
|
+
}
|
|
1901
|
+
/** The recorded change that wins the breakpoint cascade for `css` at the active
|
|
1902
|
+
* width — i.e. the value effective here, set on this breakpoint or inherited. */
|
|
1903
|
+
effectiveChange(css) {
|
|
1904
|
+
const st = this.st();
|
|
1905
|
+
if (!st) return null;
|
|
1906
|
+
const changes = st.record.changes.filter((c) => c.property === css);
|
|
1907
|
+
if (!changes.length) return null;
|
|
1908
|
+
const eff = effectiveBreakpoint(
|
|
1909
|
+
changes.map((c) => c.breakpoint),
|
|
1910
|
+
this.activeWidth(),
|
|
1911
|
+
this.bpSystem()
|
|
1912
|
+
);
|
|
1913
|
+
return eff ? changes.find((c) => c.breakpoint === eff) ?? null : null;
|
|
1914
|
+
}
|
|
1915
|
+
/** If `css`'s effective value is INHERITED from another breakpoint, its name. */
|
|
1916
|
+
inheritedFrom(props) {
|
|
1917
|
+
const scope = this.activeScope();
|
|
1918
|
+
for (const p of props) {
|
|
1919
|
+
const e = this.effectiveChange(p);
|
|
1920
|
+
if (e && e.breakpoint !== scope.name) return e.breakpoint;
|
|
1921
|
+
}
|
|
1922
|
+
return null;
|
|
1923
|
+
}
|
|
1835
1924
|
recordProps(cssList) {
|
|
1836
1925
|
const el = this.selected;
|
|
1837
1926
|
const st = this.st();
|
|
@@ -1935,6 +2024,7 @@ var Uivisor = class {
|
|
|
1935
2024
|
<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>
|
|
1936
2025
|
${this.journalHtml()}
|
|
1937
2026
|
`;
|
|
2027
|
+
if (this.responsive) this.renderFrameBar();
|
|
1938
2028
|
this.bindControls();
|
|
1939
2029
|
return;
|
|
1940
2030
|
}
|
|
@@ -1954,6 +2044,7 @@ var Uivisor = class {
|
|
|
1954
2044
|
${this.controlsHtml(this.context(this.selected))}
|
|
1955
2045
|
${this.journalHtml()}
|
|
1956
2046
|
`;
|
|
2047
|
+
if (this.responsive) this.renderFrameBar();
|
|
1957
2048
|
this.bindControls();
|
|
1958
2049
|
}
|
|
1959
2050
|
/** Small indicator: how many design tokens were detected (or a hint if none). */
|
|
@@ -2013,12 +2104,20 @@ var Uivisor = class {
|
|
|
2013
2104
|
return out;
|
|
2014
2105
|
}
|
|
2015
2106
|
/** State class for an editable control's row: edited (green, at this breakpoint)
|
|
2016
|
-
* ·
|
|
2107
|
+
* · inherit (a value cascaded from another breakpoint) · file (white, authored
|
|
2108
|
+
* in CSS) · auto (grey, browser-computed/default). */
|
|
2017
2109
|
controlStateClass(props) {
|
|
2018
2110
|
if (this.isChanged(props)) return " st-edited";
|
|
2111
|
+
if (this.inheritedFrom(props)) return " st-inherit";
|
|
2019
2112
|
const inherit = props.some((p) => INHERITED_PROPS.has(p));
|
|
2020
2113
|
return this.isAuthored(props, inherit) ? " st-file" : " st-auto";
|
|
2021
2114
|
}
|
|
2115
|
+
/** A control's label, with an "inherited from {bp}" badge when the value cascaded. */
|
|
2116
|
+
ctlLabel(label, props) {
|
|
2117
|
+
const from = this.inheritedFrom(props);
|
|
2118
|
+
const badge = from ? ` <span class="uiv-inh" title="inherited from ${escapeAttr(from)} \u2014 not set at this breakpoint">\u2923${escapeHtml(from)}</span>` : "";
|
|
2119
|
+
return `<span class="clabel">${label}${badge}</span>`;
|
|
2120
|
+
}
|
|
2022
2121
|
/** Is any of `props` authored in the project CSS? For inherited properties we
|
|
2023
2122
|
* also walk ancestors (a body/parent font rule still counts as "from the file"). */
|
|
2024
2123
|
isAuthored(props, inherit) {
|
|
@@ -2151,23 +2250,46 @@ var Uivisor = class {
|
|
|
2151
2250
|
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("");
|
|
2152
2251
|
return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
|
|
2153
2252
|
}
|
|
2154
|
-
/**
|
|
2155
|
-
|
|
2253
|
+
/** A device icon for a breakpoint chip, by its threshold (size proxy). */
|
|
2254
|
+
bpIcon(name) {
|
|
2255
|
+
if (name === "live") return ICONS.live;
|
|
2256
|
+
const px2 = name === "base" ? 0 : this.bpSystem().breakpoints.find((b) => b.name === name)?.minWidth ?? 0;
|
|
2257
|
+
if (px2 < 768) return ICONS.phone;
|
|
2258
|
+
if (px2 < 1024) return ICONS.tablet;
|
|
2259
|
+
return ICONS.desktop;
|
|
2260
|
+
}
|
|
2261
|
+
/** Breakpoint chips (Live + each project breakpoint) with device icons. Reused by
|
|
2262
|
+
* the panel (Live mode) and the bar over the virtual screen (responsive mode). */
|
|
2263
|
+
breakpointChipsHtml() {
|
|
2156
2264
|
const sys = this.bpSystem();
|
|
2157
|
-
const
|
|
2158
|
-
const names = ["base", ...bps.map((b) => b.name)];
|
|
2265
|
+
const names = ["base", ...sys.breakpoints.map((b) => b.name)];
|
|
2159
2266
|
const frameBp = this.responsive ? activeBreakpoint(this.frameWidth, sys).name : null;
|
|
2160
2267
|
const winBp = currentBreakpoint(sys).name;
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2268
|
+
const chip = (n, on, title) => `<button class="uiv-chip${on ? " on" : ""}" data-bp="${n}" title="${escapeAttr(title)}">${this.bpIcon(n)}<span>${n === "live" ? "Live" : n}</span></button>`;
|
|
2269
|
+
const live = chip("live", !this.responsive, "Follow your real browser window");
|
|
2163
2270
|
const chips = names.map((n) => {
|
|
2164
2271
|
const active = this.responsive ? n === frameBp : n === winBp;
|
|
2165
|
-
const px2 = n === "base" ? 0 :
|
|
2166
|
-
return
|
|
2272
|
+
const px2 = n === "base" ? 0 : sys.breakpoints.find((b) => b.name === n).minWidth;
|
|
2273
|
+
return chip(n, active, sys.dir === "min" ? `\u2265 ${px2}px` : `\u2264 ${px2}px`);
|
|
2167
2274
|
}).join("");
|
|
2275
|
+
return live + chips;
|
|
2276
|
+
}
|
|
2277
|
+
/** Panel breakpoint bar — shown only in Live mode (in responsive mode the bar
|
|
2278
|
+
* lives over the virtual screen instead). */
|
|
2279
|
+
breakpointBarHtml() {
|
|
2280
|
+
if (this.responsive) return "";
|
|
2281
|
+
const sys = this.bpSystem();
|
|
2282
|
+
const winBp = currentBreakpoint(sys).name;
|
|
2283
|
+
const liveW = typeof window !== "undefined" ? window.innerWidth : 0;
|
|
2168
2284
|
const detected = sys.name === "detected" ? "" : " (defaults)";
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2285
|
+
const cascade = sys.dir === "min" ? `Mobile-first: an edit applies to this breakpoint and <b>wider</b>.` : `Desktop-first: an edit applies to this breakpoint and <b>narrower</b>.`;
|
|
2286
|
+
const hint = `Live \u2014 window <b>${liveW}px</b> = <b>${winBp}</b>. ${cascade} Click a size to shrink the screen.`;
|
|
2287
|
+
return `<div class="uiv-sec"><div class="uiv-sectitle">Screen / breakpoint${detected}</div><div class="uiv-chips">${this.breakpointChipsHtml()}</div><div class="uiv-bphint">${hint}</div></div>`;
|
|
2288
|
+
}
|
|
2289
|
+
/** Populate the bar over the virtual screen (responsive mode) with the chips. */
|
|
2290
|
+
renderFrameBar() {
|
|
2291
|
+
const host = this.root.querySelector(".uiv-framechips");
|
|
2292
|
+
if (host) host.innerHTML = this.breakpointChipsHtml();
|
|
2171
2293
|
}
|
|
2172
2294
|
/** "Apply changes to": this element, an existing shared class, or a NEW class. */
|
|
2173
2295
|
targetHtml(st) {
|
|
@@ -2202,7 +2324,7 @@ var Uivisor = class {
|
|
|
2202
2324
|
return true;
|
|
2203
2325
|
}
|
|
2204
2326
|
controlsHtml(ctx) {
|
|
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>`;
|
|
2327
|
+
const legend = `<div class="uiv-leg"><span class="uiv-lg">file</span><span class="uiv-lg edit">edited</span><span class="uiv-lg inh">inherited</span><span class="uiv-lg auto">auto</span></div>`;
|
|
2206
2328
|
const secs = SECTIONS.map((sec) => {
|
|
2207
2329
|
const controls = sec.controls.filter((c) => this.relevant(c, ctx));
|
|
2208
2330
|
if (!controls.length) return "";
|
|
@@ -2284,7 +2406,7 @@ var Uivisor = class {
|
|
|
2284
2406
|
const d = this.dimDisplay(c);
|
|
2285
2407
|
const changed = this.isChanged([c.css]);
|
|
2286
2408
|
const units = c.units.map((u) => `<option value="${u}"${u === d.unit ? " selected" : ""}>${UNIT_LABELS[u] ?? u}</option>`).join("");
|
|
2287
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2409
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}">${this.ctlLabel(c.label, [c.css])}<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>`;
|
|
2288
2410
|
}
|
|
2289
2411
|
/** A design-token picker row for a property, shown only when the project exposes
|
|
2290
2412
|
* tokens for that category. Picking a token applies its value + tags the prompt. */
|
|
@@ -2318,7 +2440,7 @@ var Uivisor = class {
|
|
|
2318
2440
|
const info = this.numInfo(cssList);
|
|
2319
2441
|
const changed = this.isChanged(cssList);
|
|
2320
2442
|
const open = this.expanded.has(c.key);
|
|
2321
|
-
let html = `<div class="uiv-ctl${this.controlStateClass(cssList)}"
|
|
2443
|
+
let html = `<div class="uiv-ctl${this.controlStateClass(cssList)}">` + this.ctlLabel(c.label, cssList) + `<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>`;
|
|
2322
2444
|
if (open) {
|
|
2323
2445
|
html += `<div class="uiv-sides">` + c.sides.map((s) => {
|
|
2324
2446
|
const v = this.liveNum(s.css);
|
|
@@ -2336,7 +2458,7 @@ var Uivisor = class {
|
|
|
2336
2458
|
}
|
|
2337
2459
|
if (c.kind === "len") {
|
|
2338
2460
|
const v = this.liveNum(c.css);
|
|
2339
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2461
|
+
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");
|
|
2340
2462
|
}
|
|
2341
2463
|
if (c.kind === "dim") {
|
|
2342
2464
|
return this.dimField(c);
|
|
@@ -2345,10 +2467,10 @@ var Uivisor = class {
|
|
|
2345
2467
|
const cur = this.selectCurrent(c.css);
|
|
2346
2468
|
const optList = cur && !c.options.includes(cur) ? [cur, ...c.options] : c.options;
|
|
2347
2469
|
const opts = optList.map((o) => `<option value="${o}"${o === cur ? " selected" : ""}>${o}</option>`).join("");
|
|
2348
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2470
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}">${this.ctlLabel(c.label, [c.css])}<div class="cfield"><select class="uiv-sel${this.isChanged([c.css]) ? " changed" : ""}" data-css="${c.css}">${opts}</select></div><span></span></div>`;
|
|
2349
2471
|
}
|
|
2350
2472
|
const val = toHexInput(this.liveVal(c.css));
|
|
2351
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2473
|
+
return `<div class="uiv-ctl${this.controlStateClass([c.css])}">${this.ctlLabel(c.label, [c.css])}<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");
|
|
2352
2474
|
}
|
|
2353
2475
|
bindControls() {
|
|
2354
2476
|
const root = this.root;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uivisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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",
|