uivisor 0.1.7 → 0.1.9
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 +167 -41
- 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,12 @@ 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"/>'),
|
|
414
|
+
all: sv('<path d="M8 2.5v11 M3.2 5.2l9.6 5.6 M12.8 5.2l-9.6 5.6"/>')
|
|
370
415
|
};
|
|
371
416
|
var SECTIONS = [
|
|
372
417
|
{
|
|
@@ -1082,12 +1127,14 @@ var CSS = (
|
|
|
1082
1127
|
}
|
|
1083
1128
|
.uiv-framewrap.show { display: flex; }
|
|
1084
1129
|
.uiv-framebar {
|
|
1085
|
-
display: flex; align-items: center;
|
|
1086
|
-
height:
|
|
1130
|
+
display: flex; align-items: center; gap: 12px;
|
|
1131
|
+
height: 46px; padding: 0 14px; color: #e4e4e7; font-size: 12px; flex: 0 0 auto;
|
|
1087
1132
|
border-bottom: 1px solid #27272a;
|
|
1088
1133
|
}
|
|
1089
|
-
.uiv-
|
|
1090
|
-
.uiv-
|
|
1134
|
+
.uiv-framechips { display: flex; gap: 6px; flex: 1; justify-content: center; flex-wrap: wrap; }
|
|
1135
|
+
.uiv-framew { font-family: ui-monospace, monospace; color: #c7d2fe; font-weight: 600;
|
|
1136
|
+
white-space: nowrap; flex: 0 0 auto; }
|
|
1137
|
+
.uiv-framex { cursor: pointer; color: #a1a1aa; font-size: 14px; flex: 0 0 auto; }
|
|
1091
1138
|
.uiv-framex:hover { color: #fff; }
|
|
1092
1139
|
.uiv-framestage {
|
|
1093
1140
|
flex: 1; display: flex; align-items: stretch; justify-content: center;
|
|
@@ -1151,7 +1198,10 @@ var CSS = (
|
|
|
1151
1198
|
border-radius: 6px; padding: 3px 8px; font-size: 11px; font-weight: 600;
|
|
1152
1199
|
font-family: ui-monospace, monospace;
|
|
1153
1200
|
}
|
|
1201
|
+
.uiv-chip { display: inline-flex; align-items: center; gap: 4px; }
|
|
1202
|
+
.uiv-chip svg { width: 13px; height: 13px; opacity: .85; }
|
|
1154
1203
|
.uiv-chip:hover, .uiv-clschip:hover { color: #fff; background: #3f3f46; }
|
|
1204
|
+
.uiv-chip.on svg { opacity: 1; }
|
|
1155
1205
|
.uiv-chip.win { border-color: #52525b; color: #d4d4d8; }
|
|
1156
1206
|
.uiv-chip.on, .uiv-clschip.on { background: #4f46e5; border-color: #6366f1; color: #fff; }
|
|
1157
1207
|
.uiv-bphint { margin-top: 7px; font-size: 10px; color: #71717a; line-height: 1.4; }
|
|
@@ -1196,11 +1246,15 @@ var CSS = (
|
|
|
1196
1246
|
.uiv-ctl.st-file > .clabel { color: #e4e4e7; }
|
|
1197
1247
|
.uiv-ctl.st-edited > .clabel { color: #4ade80; }
|
|
1198
1248
|
.uiv-ctl.st-auto > .clabel { color: #6b7280; }
|
|
1249
|
+
.uiv-ctl.st-inherit > .clabel { color: #38bdf8; } /* value cascaded from another bp */
|
|
1250
|
+
.uiv-inh { font-size: 9px; font-weight: 700; color: #38bdf8; font-family: ui-monospace, monospace;
|
|
1251
|
+
background: #0c4a6e55; border: 1px solid #0369a1; border-radius: 4px; padding: 0 3px; margin-left: 2px; }
|
|
1199
1252
|
.uiv-leg { display: flex; gap: 12px; padding: 8px 12px 2px; font-size: 9px;
|
|
1200
1253
|
text-transform: uppercase; letter-spacing: .4px; }
|
|
1201
1254
|
.uiv-lg { color: #e4e4e7; display: flex; align-items: center; gap: 4px; } /* file = white */
|
|
1202
1255
|
.uiv-lg::before { content: ''; width: 7px; height: 7px; border-radius: 2px; background: currentColor; }
|
|
1203
1256
|
.uiv-lg.edit { color: #4ade80; }
|
|
1257
|
+
.uiv-lg.inh { color: #38bdf8; }
|
|
1204
1258
|
.uiv-lg.auto { color: #6b7280; }
|
|
1205
1259
|
.uiv-sw { display: inline-block; width: 11px; height: 11px; border-radius: 3px;
|
|
1206
1260
|
border: 1px solid rgba(255,255,255,0.2); flex: 0 0 auto; }
|
|
@@ -1458,7 +1512,7 @@ var Uivisor = class {
|
|
|
1458
1512
|
this.root.innerHTML = `
|
|
1459
1513
|
<style>${CSS}</style>
|
|
1460
1514
|
<div class="uiv-framewrap">
|
|
1461
|
-
<div class="uiv-framebar"><span class="uiv-framew">768px</span><span class="uiv-framex" title="Exit responsive">\u2715
|
|
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>
|
|
1462
1516
|
<div class="uiv-framestage">
|
|
1463
1517
|
<div class="uiv-framehost">
|
|
1464
1518
|
<iframe class="uiv-frame" data-uiv-frame="1"></iframe>
|
|
@@ -1643,7 +1697,7 @@ var Uivisor = class {
|
|
|
1643
1697
|
const host = this.q(".uiv-framehost");
|
|
1644
1698
|
host.style.width = `${this.frameWidth}px`;
|
|
1645
1699
|
const bp = activeBreakpoint(this.frameWidth, this.bpSystem()).name;
|
|
1646
|
-
this.q(".uiv-framew").textContent = `${this.frameWidth}px \xB7 ${bp}`;
|
|
1700
|
+
this.q(".uiv-framew").textContent = `${this.frameWidth}px \xB7 ${this.bpLabel(bp)}`;
|
|
1647
1701
|
this.updateBp();
|
|
1648
1702
|
this.reposition();
|
|
1649
1703
|
}
|
|
@@ -1724,8 +1778,7 @@ var Uivisor = class {
|
|
|
1724
1778
|
const el = this.selected;
|
|
1725
1779
|
const st = this.st();
|
|
1726
1780
|
if (!el || !st) return "";
|
|
1727
|
-
const
|
|
1728
|
-
const ch = st.record.changes.find((c) => c.property === css && c.breakpoint === scope.name);
|
|
1781
|
+
const ch = this.effectiveChange(css);
|
|
1729
1782
|
if (ch) {
|
|
1730
1783
|
const v = ch.live ?? ch.after.computed;
|
|
1731
1784
|
return v.includes("var(") ? this.computedVal(css) || v : v;
|
|
@@ -1810,16 +1863,25 @@ var Uivisor = class {
|
|
|
1810
1863
|
* inline override leaking across all of them.
|
|
1811
1864
|
*/
|
|
1812
1865
|
reapplyScope() {
|
|
1813
|
-
const
|
|
1866
|
+
const width = this.activeWidth();
|
|
1867
|
+
const sys = this.bpSystem();
|
|
1814
1868
|
for (const [el, st] of this.states) {
|
|
1815
1869
|
const sibs = this.siblingsOf(el);
|
|
1816
1870
|
const targets = st.record.target === "all" ? sibs : [el];
|
|
1817
1871
|
for (const css of st.applied) for (const e of sibs) removeOverride(e, css);
|
|
1818
1872
|
st.applied = /* @__PURE__ */ new Set();
|
|
1873
|
+
const byProp = /* @__PURE__ */ new Map();
|
|
1819
1874
|
for (const c of st.record.changes) {
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1875
|
+
const arr = byProp.get(c.property) ?? [];
|
|
1876
|
+
arr.push(c);
|
|
1877
|
+
byProp.set(c.property, arr);
|
|
1878
|
+
}
|
|
1879
|
+
for (const [prop, changes] of byProp) {
|
|
1880
|
+
const eff = effectiveBreakpoint(changes.map((c2) => c2.breakpoint), width, sys);
|
|
1881
|
+
if (!eff) continue;
|
|
1882
|
+
const c = changes.find((x) => x.breakpoint === eff);
|
|
1883
|
+
for (const e of targets) applyOverride(e, prop, c.live ?? c.after.computed);
|
|
1884
|
+
st.applied.add(prop);
|
|
1823
1885
|
}
|
|
1824
1886
|
}
|
|
1825
1887
|
this.reposition();
|
|
@@ -1832,6 +1894,34 @@ var Uivisor = class {
|
|
|
1832
1894
|
if (this.responsive) return activeBreakpoint(this.frameWidth, sys);
|
|
1833
1895
|
return currentBreakpoint(sys);
|
|
1834
1896
|
}
|
|
1897
|
+
/** The width the inspector is scoped to (virtual screen, else real window). */
|
|
1898
|
+
activeWidth() {
|
|
1899
|
+
if (this.responsive) return this.frameWidth;
|
|
1900
|
+
return typeof window !== "undefined" ? window.innerWidth : 0;
|
|
1901
|
+
}
|
|
1902
|
+
/** The recorded change that wins the breakpoint cascade for `css` at the active
|
|
1903
|
+
* width — i.e. the value effective here, set on this breakpoint or inherited. */
|
|
1904
|
+
effectiveChange(css) {
|
|
1905
|
+
const st = this.st();
|
|
1906
|
+
if (!st) return null;
|
|
1907
|
+
const changes = st.record.changes.filter((c) => c.property === css);
|
|
1908
|
+
if (!changes.length) return null;
|
|
1909
|
+
const eff = effectiveBreakpoint(
|
|
1910
|
+
changes.map((c) => c.breakpoint),
|
|
1911
|
+
this.activeWidth(),
|
|
1912
|
+
this.bpSystem()
|
|
1913
|
+
);
|
|
1914
|
+
return eff ? changes.find((c) => c.breakpoint === eff) ?? null : null;
|
|
1915
|
+
}
|
|
1916
|
+
/** If `css`'s effective value is INHERITED from another breakpoint, its name. */
|
|
1917
|
+
inheritedFrom(props) {
|
|
1918
|
+
const scope = this.activeScope();
|
|
1919
|
+
for (const p of props) {
|
|
1920
|
+
const e = this.effectiveChange(p);
|
|
1921
|
+
if (e && e.breakpoint !== scope.name) return e.breakpoint;
|
|
1922
|
+
}
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1835
1925
|
recordProps(cssList) {
|
|
1836
1926
|
const el = this.selected;
|
|
1837
1927
|
const st = this.st();
|
|
@@ -1935,6 +2025,7 @@ var Uivisor = class {
|
|
|
1935
2025
|
<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
2026
|
${this.journalHtml()}
|
|
1937
2027
|
`;
|
|
2028
|
+
if (this.responsive) this.renderFrameBar();
|
|
1938
2029
|
this.bindControls();
|
|
1939
2030
|
return;
|
|
1940
2031
|
}
|
|
@@ -1954,6 +2045,7 @@ var Uivisor = class {
|
|
|
1954
2045
|
${this.controlsHtml(this.context(this.selected))}
|
|
1955
2046
|
${this.journalHtml()}
|
|
1956
2047
|
`;
|
|
2048
|
+
if (this.responsive) this.renderFrameBar();
|
|
1957
2049
|
this.bindControls();
|
|
1958
2050
|
}
|
|
1959
2051
|
/** Small indicator: how many design tokens were detected (or a hint if none). */
|
|
@@ -2013,12 +2105,20 @@ var Uivisor = class {
|
|
|
2013
2105
|
return out;
|
|
2014
2106
|
}
|
|
2015
2107
|
/** State class for an editable control's row: edited (green, at this breakpoint)
|
|
2016
|
-
* ·
|
|
2108
|
+
* · inherit (a value cascaded from another breakpoint) · file (white, authored
|
|
2109
|
+
* in CSS) · auto (grey, browser-computed/default). */
|
|
2017
2110
|
controlStateClass(props) {
|
|
2018
2111
|
if (this.isChanged(props)) return " st-edited";
|
|
2112
|
+
if (this.inheritedFrom(props)) return " st-inherit";
|
|
2019
2113
|
const inherit = props.some((p) => INHERITED_PROPS.has(p));
|
|
2020
2114
|
return this.isAuthored(props, inherit) ? " st-file" : " st-auto";
|
|
2021
2115
|
}
|
|
2116
|
+
/** A control's label, with an "inherited from {bp}" badge when the value cascaded. */
|
|
2117
|
+
ctlLabel(label, props) {
|
|
2118
|
+
const from = this.inheritedFrom(props);
|
|
2119
|
+
const badge = from ? ` <span class="uiv-inh" title="inherited from ${escapeAttr(this.bpLabel(from))} \u2014 not set at this breakpoint">\u2923${escapeHtml(this.bpLabel(from))}</span>` : "";
|
|
2120
|
+
return `<span class="clabel">${label}${badge}</span>`;
|
|
2121
|
+
}
|
|
2022
2122
|
/** Is any of `props` authored in the project CSS? For inherited properties we
|
|
2023
2123
|
* also walk ancestors (a body/parent font rule still counts as "from the file"). */
|
|
2024
2124
|
isAuthored(props, inherit) {
|
|
@@ -2151,23 +2251,49 @@ var Uivisor = class {
|
|
|
2151
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("");
|
|
2152
2252
|
return `<div class="uiv-sec">${this.accordionTitle("Current styles")}<div class="uiv-readout">${items}</div></div>`;
|
|
2153
2253
|
}
|
|
2154
|
-
/**
|
|
2155
|
-
|
|
2254
|
+
/** Display label for a breakpoint name — the unprefixed "base" scope reads "all"
|
|
2255
|
+
* (applies to every size by default); internal key stays "base". */
|
|
2256
|
+
bpLabel(name) {
|
|
2257
|
+
return name === "base" ? "all" : name;
|
|
2258
|
+
}
|
|
2259
|
+
/** A device icon for a breakpoint chip, by its threshold (size proxy). */
|
|
2260
|
+
bpIcon(name) {
|
|
2261
|
+
if (name === "live") return ICONS.live;
|
|
2262
|
+
if (name === "base") return ICONS.all;
|
|
2263
|
+
const px2 = this.bpSystem().breakpoints.find((b) => b.name === name)?.minWidth ?? 0;
|
|
2264
|
+
if (px2 < 768) return ICONS.phone;
|
|
2265
|
+
if (px2 < 1024) return ICONS.tablet;
|
|
2266
|
+
return ICONS.desktop;
|
|
2267
|
+
}
|
|
2268
|
+
/** Breakpoint chips with icons. Order: "all" (base, the default) → Live → each
|
|
2269
|
+
* project breakpoint. Reused by the panel (Live) and the over-frame bar. */
|
|
2270
|
+
breakpointChipsHtml() {
|
|
2156
2271
|
const sys = this.bpSystem();
|
|
2157
|
-
const bps = sys.breakpoints;
|
|
2158
|
-
const names = ["base", ...bps.map((b) => b.name)];
|
|
2159
2272
|
const frameBp = this.responsive ? activeBreakpoint(this.frameWidth, sys).name : null;
|
|
2160
2273
|
const winBp = currentBreakpoint(sys).name;
|
|
2274
|
+
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>`;
|
|
2276
|
+
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
|
+
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;
|
|
2280
|
+
}
|
|
2281
|
+
/** Panel breakpoint bar — shown only in Live mode (in responsive mode the bar
|
|
2282
|
+
* lives over the virtual screen instead). */
|
|
2283
|
+
breakpointBarHtml() {
|
|
2284
|
+
if (this.responsive) return "";
|
|
2285
|
+
const sys = this.bpSystem();
|
|
2286
|
+
const winBp = currentBreakpoint(sys).name;
|
|
2161
2287
|
const liveW = typeof window !== "undefined" ? window.innerWidth : 0;
|
|
2162
|
-
const liveChip = `<button class="uiv-chip${!this.responsive ? " on" : ""}" data-bp="live" title="Follow your real browser window">Live</button>`;
|
|
2163
|
-
const chips = names.map((n) => {
|
|
2164
|
-
const active = this.responsive ? n === frameBp : n === winBp;
|
|
2165
|
-
const px2 = n === "base" ? 0 : bps.find((b) => b.name === n).minWidth;
|
|
2166
|
-
return `<button class="uiv-chip${active ? " on" : ""}" data-bp="${n}" title="Preview at \u2265${px2}px">${n}</button>`;
|
|
2167
|
-
}).join("");
|
|
2168
2288
|
const detected = sys.name === "detected" ? "" : " (defaults)";
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2289
|
+
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>.`;
|
|
2290
|
+
const hint = `Live \u2014 window <b>${liveW}px</b> = <b>${this.bpLabel(winBp)}</b>. ${cascade} Click a size to shrink the screen.`;
|
|
2291
|
+
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>`;
|
|
2292
|
+
}
|
|
2293
|
+
/** Populate the bar over the virtual screen (responsive mode) with the chips. */
|
|
2294
|
+
renderFrameBar() {
|
|
2295
|
+
const host = this.root.querySelector(".uiv-framechips");
|
|
2296
|
+
if (host) host.innerHTML = this.breakpointChipsHtml();
|
|
2171
2297
|
}
|
|
2172
2298
|
/** "Apply changes to": this element, an existing shared class, or a NEW class. */
|
|
2173
2299
|
targetHtml(st) {
|
|
@@ -2202,7 +2328,7 @@ var Uivisor = class {
|
|
|
2202
2328
|
return true;
|
|
2203
2329
|
}
|
|
2204
2330
|
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>`;
|
|
2331
|
+
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
2332
|
const secs = SECTIONS.map((sec) => {
|
|
2207
2333
|
const controls = sec.controls.filter((c) => this.relevant(c, ctx));
|
|
2208
2334
|
if (!controls.length) return "";
|
|
@@ -2284,7 +2410,7 @@ var Uivisor = class {
|
|
|
2284
2410
|
const d = this.dimDisplay(c);
|
|
2285
2411
|
const changed = this.isChanged([c.css]);
|
|
2286
2412
|
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])}"
|
|
2413
|
+
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
2414
|
}
|
|
2289
2415
|
/** A design-token picker row for a property, shown only when the project exposes
|
|
2290
2416
|
* tokens for that category. Picking a token applies its value + tags the prompt. */
|
|
@@ -2318,7 +2444,7 @@ var Uivisor = class {
|
|
|
2318
2444
|
const info = this.numInfo(cssList);
|
|
2319
2445
|
const changed = this.isChanged(cssList);
|
|
2320
2446
|
const open = this.expanded.has(c.key);
|
|
2321
|
-
let html = `<div class="uiv-ctl${this.controlStateClass(cssList)}"
|
|
2447
|
+
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
2448
|
if (open) {
|
|
2323
2449
|
html += `<div class="uiv-sides">` + c.sides.map((s) => {
|
|
2324
2450
|
const v = this.liveNum(s.css);
|
|
@@ -2336,7 +2462,7 @@ var Uivisor = class {
|
|
|
2336
2462
|
}
|
|
2337
2463
|
if (c.kind === "len") {
|
|
2338
2464
|
const v = this.liveNum(c.css);
|
|
2339
|
-
return `<div class="uiv-ctl${this.controlStateClass([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");
|
|
2340
2466
|
}
|
|
2341
2467
|
if (c.kind === "dim") {
|
|
2342
2468
|
return this.dimField(c);
|
|
@@ -2345,10 +2471,10 @@ var Uivisor = class {
|
|
|
2345
2471
|
const cur = this.selectCurrent(c.css);
|
|
2346
2472
|
const optList = cur && !c.options.includes(cur) ? [cur, ...c.options] : c.options;
|
|
2347
2473
|
const opts = optList.map((o) => `<option value="${o}"${o === cur ? " selected" : ""}>${o}</option>`).join("");
|
|
2348
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2474
|
+
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
2475
|
}
|
|
2350
2476
|
const val = toHexInput(this.liveVal(c.css));
|
|
2351
|
-
return `<div class="uiv-ctl${this.controlStateClass([c.css])}"
|
|
2477
|
+
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
2478
|
}
|
|
2353
2479
|
bindControls() {
|
|
2354
2480
|
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.9",
|
|
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",
|