uivisor 0.1.3 → 0.1.5
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/next/index.cjs +5 -4
- package/dist/next/index.js +5 -4
- package/dist/overlay/index.js +273 -5
- package/package.json +1 -1
package/dist/next/index.cjs
CHANGED
|
@@ -96,12 +96,13 @@ function withUivisor(nextConfig = {}, options = {}) {
|
|
|
96
96
|
}
|
|
97
97
|
if (useTurbopack) {
|
|
98
98
|
const ld = [{ loader: "uivisor/next/loader", options: { attr } }];
|
|
99
|
+
const major = nextMajor();
|
|
100
|
+
const modern = major === 0 || major >= 15;
|
|
99
101
|
const rules = {
|
|
100
|
-
"*.tsx": { loaders: ld, as: "*.tsx" },
|
|
101
|
-
"*.jsx": { loaders: ld, as: "*.jsx" }
|
|
102
|
+
"*.tsx": modern ? { loaders: ld } : { loaders: ld, as: "*.tsx" },
|
|
103
|
+
"*.jsx": modern ? { loaders: ld } : { loaders: ld, as: "*.jsx" }
|
|
102
104
|
};
|
|
103
|
-
|
|
104
|
-
if (major === 0 || major >= 15) {
|
|
105
|
+
if (modern) {
|
|
105
106
|
out.turbopack = {
|
|
106
107
|
...nextConfig.turbopack,
|
|
107
108
|
rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
|
package/dist/next/index.js
CHANGED
|
@@ -68,12 +68,13 @@ function withUivisor(nextConfig = {}, options = {}) {
|
|
|
68
68
|
}
|
|
69
69
|
if (useTurbopack) {
|
|
70
70
|
const ld = [{ loader: "uivisor/next/loader", options: { attr } }];
|
|
71
|
+
const major = nextMajor();
|
|
72
|
+
const modern = major === 0 || major >= 15;
|
|
71
73
|
const rules = {
|
|
72
|
-
"*.tsx": { loaders: ld, as: "*.tsx" },
|
|
73
|
-
"*.jsx": { loaders: ld, as: "*.jsx" }
|
|
74
|
+
"*.tsx": modern ? { loaders: ld } : { loaders: ld, as: "*.tsx" },
|
|
75
|
+
"*.jsx": modern ? { loaders: ld } : { loaders: ld, as: "*.jsx" }
|
|
74
76
|
};
|
|
75
|
-
|
|
76
|
-
if (major === 0 || major >= 15) {
|
|
77
|
+
if (modern) {
|
|
77
78
|
out.turbopack = {
|
|
78
79
|
...nextConfig.turbopack,
|
|
79
80
|
rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
|
package/dist/overlay/index.js
CHANGED
|
@@ -542,6 +542,151 @@ var SHORTHAND_SETS = [
|
|
|
542
542
|
}
|
|
543
543
|
];
|
|
544
544
|
|
|
545
|
+
// src/overlay/designtokens.ts
|
|
546
|
+
var COLOR_RE = /^(#[0-9a-f]{3,8}|rgba?\(|hsla?\()/i;
|
|
547
|
+
var LEN_RE = /^-?\d*\.?\d+(px|rem|em)$/i;
|
|
548
|
+
var SHADOW_RE = /\d+px.*(rgba?\(|hsla?\(|#[0-9a-f]{3,8})/i;
|
|
549
|
+
var FONT_STACK_RE = /(sans-serif|serif|monospace|system-ui|,)/i;
|
|
550
|
+
var isColor = (v) => COLOR_RE.test(v.trim());
|
|
551
|
+
var isLen = (v) => LEN_RE.test(v.trim());
|
|
552
|
+
function lenToPx(value, rootPx = 16) {
|
|
553
|
+
const m = /^(-?\d*\.?\d+)(px|rem|em)$/i.exec(value.trim());
|
|
554
|
+
if (!m) return null;
|
|
555
|
+
const n = parseFloat(m[1]);
|
|
556
|
+
const unit = m[2].toLowerCase();
|
|
557
|
+
if (unit === "px") return n;
|
|
558
|
+
return n * rootPx;
|
|
559
|
+
}
|
|
560
|
+
var PREFIXES = [
|
|
561
|
+
{ re: /^--font-size-/, cat: "font-size" },
|
|
562
|
+
{ re: /^--fs-/, cat: "font-size" },
|
|
563
|
+
{ re: /^--text-/, cat: "font-size" },
|
|
564
|
+
// only when value is a length (handled below)
|
|
565
|
+
{ re: /^--leading-/, cat: "line-height" },
|
|
566
|
+
{ re: /^--line-height-/, cat: "line-height" },
|
|
567
|
+
{ re: /^--tracking-/, cat: "letter-spacing" },
|
|
568
|
+
{ re: /^--letter-spacing-/, cat: "letter-spacing" },
|
|
569
|
+
{ re: /^--radius-/, cat: "radius" },
|
|
570
|
+
{ re: /^--rounded-/, cat: "radius" },
|
|
571
|
+
{ re: /^--shadow-/, cat: "shadow" },
|
|
572
|
+
{ re: /^--space-/, cat: "spacing" },
|
|
573
|
+
{ re: /^--spacing-/, cat: "spacing" },
|
|
574
|
+
{ re: /^--gap-/, cat: "spacing" },
|
|
575
|
+
{ re: /^--airy-/, cat: "spacing" },
|
|
576
|
+
{ re: /^--color-/, cat: "color" },
|
|
577
|
+
{ re: /^--bg-/, cat: "color" },
|
|
578
|
+
{ re: /^--border-/, cat: "color" },
|
|
579
|
+
{ re: /^--font-/, cat: "font-family" }
|
|
580
|
+
];
|
|
581
|
+
var FONTSIZE_HINT = /(^|[-_])(fs|font-size|text|headline|title|body|label|display|caption|heading)([-_]|$)/i;
|
|
582
|
+
var SPACING_HINT = /(^|[-_])(space|spacing|gap|airy|inset)([-_]|$)/i;
|
|
583
|
+
function classifyVar(name, value) {
|
|
584
|
+
const v = value.trim();
|
|
585
|
+
const n = name.toLowerCase();
|
|
586
|
+
if (!v) return null;
|
|
587
|
+
if (n.includes("shadow") || SHADOW_RE.test(v)) return "shadow";
|
|
588
|
+
if (isColor(v)) return "color";
|
|
589
|
+
if (FONT_STACK_RE.test(v) && !isLen(v)) {
|
|
590
|
+
if (n.includes("family") || /^--font(-|$)/.test(n) || FONT_STACK_RE.test(v)) return "font-family";
|
|
591
|
+
}
|
|
592
|
+
if (isLen(v)) {
|
|
593
|
+
if (n.includes("radius") || n.includes("rounded")) return "radius";
|
|
594
|
+
if (n.includes("leading") || n.includes("line-height")) return "line-height";
|
|
595
|
+
if (n.includes("tracking") || n.includes("letter")) return "letter-spacing";
|
|
596
|
+
if (FONTSIZE_HINT.test(n)) return "font-size";
|
|
597
|
+
if (SPACING_HINT.test(n)) return "spacing";
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
function tokenName(cssVar, _category) {
|
|
603
|
+
for (const p of PREFIXES) {
|
|
604
|
+
if (p.re.test(cssVar)) return cssVar.replace(p.re, "");
|
|
605
|
+
}
|
|
606
|
+
return cssVar.replace(/^--/, "");
|
|
607
|
+
}
|
|
608
|
+
function buildDesignSystem(vars, rootPx = 16) {
|
|
609
|
+
const tokens = [];
|
|
610
|
+
for (const [cssVar, value] of Object.entries(vars)) {
|
|
611
|
+
const category = classifyVar(cssVar, value);
|
|
612
|
+
if (!category) continue;
|
|
613
|
+
const scalar = category === "font-size" || category === "radius" || category === "spacing";
|
|
614
|
+
tokens.push({
|
|
615
|
+
cssVar,
|
|
616
|
+
name: tokenName(cssVar, category),
|
|
617
|
+
category,
|
|
618
|
+
value: value.trim(),
|
|
619
|
+
px: scalar ? lenToPx(value, rootPx) : null
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
const byCategory = {};
|
|
623
|
+
for (const t of tokens) (byCategory[t.category] ||= []).push(t);
|
|
624
|
+
for (const cat of Object.keys(byCategory)) {
|
|
625
|
+
byCategory[cat].sort(
|
|
626
|
+
(a, b) => a.px != null && b.px != null ? a.px - b.px : a.name.localeCompare(b.name)
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
return { tokens, byCategory, source: tokens.length ? "css-vars" : "none" };
|
|
630
|
+
}
|
|
631
|
+
function nearestToken(ds, category, target) {
|
|
632
|
+
const list = ds.byCategory[category];
|
|
633
|
+
if (!list || !list.length) return null;
|
|
634
|
+
if (target.value != null) {
|
|
635
|
+
const norm = (s) => s.replace(/\s+/g, "").toLowerCase();
|
|
636
|
+
const hit = list.find((t) => norm(t.value) === norm(target.value));
|
|
637
|
+
if (hit) return { token: hit, exact: true };
|
|
638
|
+
}
|
|
639
|
+
if (target.px != null && list.some((t) => t.px != null)) {
|
|
640
|
+
let best = list[0];
|
|
641
|
+
let bestD = Infinity;
|
|
642
|
+
for (const t of list) {
|
|
643
|
+
if (t.px == null) continue;
|
|
644
|
+
const d = Math.abs(t.px - target.px);
|
|
645
|
+
if (d < bestD) {
|
|
646
|
+
bestD = d;
|
|
647
|
+
best = t;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return { token: best, exact: bestD < 0.5 };
|
|
651
|
+
}
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
var SCALAR_VAR_SELECTOR = /(^|[\s,>])(:root|html|\[data-theme)/i;
|
|
655
|
+
function detectDesignSystem() {
|
|
656
|
+
if (typeof document === "undefined") return { tokens: [], byCategory: {}, source: "none" };
|
|
657
|
+
const names = /* @__PURE__ */ new Set();
|
|
658
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
659
|
+
let rules;
|
|
660
|
+
try {
|
|
661
|
+
rules = sheet.cssRules;
|
|
662
|
+
} catch {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
for (const rule of Array.from(rules)) collectVarNames(rule, names);
|
|
666
|
+
}
|
|
667
|
+
const root = document.documentElement;
|
|
668
|
+
const cs = getComputedStyle(root);
|
|
669
|
+
const rootPx = parseFloat(cs.fontSize) || 16;
|
|
670
|
+
const vars = {};
|
|
671
|
+
for (const name of names) {
|
|
672
|
+
const resolved = cs.getPropertyValue(name).trim();
|
|
673
|
+
if (resolved) vars[name] = resolved;
|
|
674
|
+
}
|
|
675
|
+
return buildDesignSystem(vars, rootPx);
|
|
676
|
+
}
|
|
677
|
+
function collectVarNames(rule, out) {
|
|
678
|
+
const style = rule.style;
|
|
679
|
+
const selector = rule.selectorText;
|
|
680
|
+
if (style && selector && SCALAR_VAR_SELECTOR.test(selector)) {
|
|
681
|
+
for (let i = 0; i < style.length; i++) {
|
|
682
|
+
const prop = style[i];
|
|
683
|
+
if (prop.startsWith("--")) out.add(prop);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const inner = rule.cssRules;
|
|
687
|
+
if (inner) for (const r of Array.from(inner)) collectVarNames(r, out);
|
|
688
|
+
}
|
|
689
|
+
|
|
545
690
|
// src/overlay/mechanism.ts
|
|
546
691
|
var TW_UTILITY = /^-?(?:p|m|px|py|pt|pr|pb|pl|mx|my|mt|mr|mb|ml|gap|w|h|min-w|max-w|min-h|max-h|text|bg|border|rounded|flex|grid|gap-x|gap-y|items|justify|self|place|font|leading|tracking|space|inline|block|inline-block|hidden|table|absolute|relative|fixed|sticky|static|top|bottom|left|right|inset|z|shadow|opacity|transition|duration|ease|scale|rotate|translate|cursor|overflow|object|aspect|order|col|row|basis|grow|shrink|divide|ring|outline)(?:-|$)/;
|
|
547
692
|
var TW_VARIANT = /^(?:sm|md|lg|xl|2xl|hover|focus|focus-visible|focus-within|active|visited|disabled|dark|group|group-hover|peer|first|last|odd|even|motion-safe|motion-reduce|print|rtl|ltr)::?|^(?:sm|md|lg|xl|2xl|hover|focus|active|disabled|dark|group-hover):/;
|
|
@@ -667,6 +812,7 @@ function renderPrompt(records) {
|
|
|
667
812
|
lines.push("");
|
|
668
813
|
let anyClassTarget = false;
|
|
669
814
|
let anyNewClass = false;
|
|
815
|
+
let anyDesignToken = false;
|
|
670
816
|
active.forEach((r, i) => {
|
|
671
817
|
const id = r.identity;
|
|
672
818
|
const newClass = r.target?.startsWith("new:") ? r.target.slice(4) : null;
|
|
@@ -718,7 +864,15 @@ function renderPrompt(records) {
|
|
|
718
864
|
const scope = bp === "base" ? "all sizes (base)" : `${bp} breakpoint (\u2265${byBp[bp][0].breakpointPx}px)`;
|
|
719
865
|
lines.push(`- At ${scope}:`);
|
|
720
866
|
for (const c of byBp[bp]) {
|
|
721
|
-
|
|
867
|
+
let suggestion = "";
|
|
868
|
+
if (c.after.token) {
|
|
869
|
+
suggestion = ` \u2192 \`${variant(bp, c.after.token)}\``;
|
|
870
|
+
} else if (c.after.designToken) {
|
|
871
|
+
const at = bp === "base" ? "" : ` (scope to ${bp})`;
|
|
872
|
+
suggestion = ` \u2192 use design token \`${c.after.designToken}\` (\`var(${c.after.designToken})\`)${at}`;
|
|
873
|
+
}
|
|
874
|
+
if (c.after.designToken || c.after.token && /^(text|bg|border|rounded|shadow)-(?!\[)/.test(c.after.token))
|
|
875
|
+
anyDesignToken = true;
|
|
722
876
|
lines.push(
|
|
723
877
|
` - ${c.property}: ${c.before.computed} \u2192 ${c.after.computed}${suggestion}`
|
|
724
878
|
);
|
|
@@ -747,6 +901,11 @@ function renderPrompt(records) {
|
|
|
747
901
|
"- When creating a new class, leave the current classes untouched and add the new class alongside them; pick the styling mechanism the project already uses."
|
|
748
902
|
);
|
|
749
903
|
}
|
|
904
|
+
if (anyDesignToken) {
|
|
905
|
+
lines.push(
|
|
906
|
+
"- Prefer the project DESIGN TOKEN shown for each change (a Tailwind utility like `text-headline-l`, or the CSS variable `var(--\u2026)`) over a hardcoded px/hex value \u2014 these come from the project\u2019s own design system."
|
|
907
|
+
);
|
|
908
|
+
}
|
|
750
909
|
return lines.join("\n");
|
|
751
910
|
}
|
|
752
911
|
function renderSpec(records, meta) {
|
|
@@ -1002,6 +1161,19 @@ var CSS = (
|
|
|
1002
1161
|
.uiv-newclass:focus { border-style: solid; border-color: #6366f1; color: #fff; }
|
|
1003
1162
|
.uiv-newclass.on { border-style: solid; border-color: #22d3ee; color: #fff; }
|
|
1004
1163
|
|
|
1164
|
+
/* ---- design-system indicator + token pickers ---- */
|
|
1165
|
+
.uiv-dsbar { padding: 7px 12px; border-bottom: 1px solid #27272a;
|
|
1166
|
+
font-size: 10px; font-weight: 600; letter-spacing: .3px; color: #a5b4fc;
|
|
1167
|
+
background: #1e1b4b33; display: flex; align-items: center; gap: 6px; }
|
|
1168
|
+
.uiv-tlabel { color: #818cf8 !important; font-size: 10px; }
|
|
1169
|
+
.uiv-ctl select.uiv-tokensel {
|
|
1170
|
+
width: 100%; background: #1e1b4b55; border: 1px solid #4338ca; color: #c7d2fe;
|
|
1171
|
+
border-radius: 7px; padding: 6px 7px; font-size: 11px; outline: none;
|
|
1172
|
+
font-family: ui-monospace, monospace; }
|
|
1173
|
+
.uiv-ctl select.uiv-tokensel:hover { border-color: #6366f1; color: #fff; }
|
|
1174
|
+
.uiv-ctl select.uiv-tokensel:focus { border-color: #818cf8; }
|
|
1175
|
+
.uiv-ctl select.uiv-tokensel.changed { border-color: #4ade80; color: #86efac; }
|
|
1176
|
+
|
|
1005
1177
|
/* ---- current-styles readout ---- */
|
|
1006
1178
|
.uiv-readout { display: flex; flex-direction: column; gap: 3px; }
|
|
1007
1179
|
.uiv-rrow { display: grid; grid-template-columns: 70px 1fr; gap: 8px; align-items: center;
|
|
@@ -1126,6 +1298,10 @@ var Uivisor = class {
|
|
|
1126
1298
|
this._csEl = null;
|
|
1127
1299
|
/** Cached project breakpoint system (detected from CSS), refreshed until found. */
|
|
1128
1300
|
this._bp = null;
|
|
1301
|
+
/** Cached project design system (detected from CSS variables), refreshed until found. */
|
|
1302
|
+
this._ds = null;
|
|
1303
|
+
/** Memoised Tailwind-utility probes: candidate class → resolved class or null. */
|
|
1304
|
+
this.utilCache = /* @__PURE__ */ new Map();
|
|
1129
1305
|
// responsive (virtual screen) mode
|
|
1130
1306
|
this.responsive = false;
|
|
1131
1307
|
this.frameWidth = 768;
|
|
@@ -1201,6 +1377,43 @@ var Uivisor = class {
|
|
|
1201
1377
|
if (!this._bp || this._bp.name !== "detected") this._bp = detectBreakpoints();
|
|
1202
1378
|
return this._bp;
|
|
1203
1379
|
}
|
|
1380
|
+
/** Project design tokens — re-detect until the CSS variables resolve. */
|
|
1381
|
+
designSystem() {
|
|
1382
|
+
if (!this._ds || this._ds.source === "none") this._ds = detectDesignSystem();
|
|
1383
|
+
return this._ds;
|
|
1384
|
+
}
|
|
1385
|
+
/** Which token category (if any) a CSS property picks tokens from. */
|
|
1386
|
+
dsCategoryFor(css) {
|
|
1387
|
+
if (css === "font-size") return "font-size";
|
|
1388
|
+
if (css === "color" || css === "background-color") return "color";
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
/** Does a Tailwind utility for this token actually exist in the project CSS?
|
|
1392
|
+
* Probe a hidden element in the page document and compare the resolved value. */
|
|
1393
|
+
dsUtility(css, token) {
|
|
1394
|
+
const prefix = css === "background-color" ? "bg" : "text";
|
|
1395
|
+
const cls = `${prefix}-${token.name}`;
|
|
1396
|
+
if (this.utilCache.has(cls)) return this.utilCache.get(cls) ?? null;
|
|
1397
|
+
let res = null;
|
|
1398
|
+
try {
|
|
1399
|
+
const probe = document.createElement("span");
|
|
1400
|
+
probe.className = cls;
|
|
1401
|
+
probe.style.cssText = "position:absolute;left:-99999px;top:0;visibility:hidden";
|
|
1402
|
+
document.body.appendChild(probe);
|
|
1403
|
+
const got = getComputedStyle(probe).getPropertyValue(css).trim();
|
|
1404
|
+
probe.remove();
|
|
1405
|
+
if (token.category === "color") {
|
|
1406
|
+
const a = (rgbToHex(got) || got).toLowerCase();
|
|
1407
|
+
const b = (rgbToHex(token.value) || token.value).toLowerCase();
|
|
1408
|
+
if (a && a === b) res = cls;
|
|
1409
|
+
} else if (token.px != null && Math.abs(parseFloat(got) - token.px) < 0.5) {
|
|
1410
|
+
res = cls;
|
|
1411
|
+
}
|
|
1412
|
+
} catch {
|
|
1413
|
+
}
|
|
1414
|
+
this.utilCache.set(cls, res);
|
|
1415
|
+
return res;
|
|
1416
|
+
}
|
|
1204
1417
|
mount() {
|
|
1205
1418
|
this.host = document.createElement("div");
|
|
1206
1419
|
this.host.id = "uivisor-root";
|
|
@@ -1309,7 +1522,10 @@ var Uivisor = class {
|
|
|
1309
1522
|
const prev = this._bp;
|
|
1310
1523
|
this._bp = null;
|
|
1311
1524
|
const next = this.bpSystem();
|
|
1312
|
-
|
|
1525
|
+
const prevDsN = this._ds?.tokens.length ?? -1;
|
|
1526
|
+
this._ds = null;
|
|
1527
|
+
const nextDs = this.designSystem();
|
|
1528
|
+
if (!prev || key(prev) !== key(next) || prevDsN !== nextDs.tokens.length) this.renderBody();
|
|
1313
1529
|
};
|
|
1314
1530
|
for (const d of [250, 900, 2200]) window.setTimeout(refresh, d);
|
|
1315
1531
|
}
|
|
@@ -1570,6 +1786,22 @@ var Uivisor = class {
|
|
|
1570
1786
|
}
|
|
1571
1787
|
this.renderBody();
|
|
1572
1788
|
}
|
|
1789
|
+
/** Apply a design-system token to a property: set its resolved value live and
|
|
1790
|
+
* annotate the recorded change so the prompt asks the agent for the token. */
|
|
1791
|
+
applyToken(css, token) {
|
|
1792
|
+
this.pushHistory();
|
|
1793
|
+
this.liveSet([css], token.value);
|
|
1794
|
+
this.recordProps([css]);
|
|
1795
|
+
const st = this.st();
|
|
1796
|
+
if (!st) return;
|
|
1797
|
+
const scope = this.activeScope();
|
|
1798
|
+
const ch = st.record.changes.find((c) => c.property === css && c.breakpoint === scope.name);
|
|
1799
|
+
if (ch) {
|
|
1800
|
+
ch.after.designToken = token.cssVar;
|
|
1801
|
+
ch.after.token = st.record.styling.primaryMechanism === "tailwind" ? this.dsUtility(css, token) : null;
|
|
1802
|
+
}
|
|
1803
|
+
this.renderBody();
|
|
1804
|
+
}
|
|
1573
1805
|
revertProps(cssList) {
|
|
1574
1806
|
const el = this.selected;
|
|
1575
1807
|
const st = this.st();
|
|
@@ -1651,6 +1883,7 @@ var Uivisor = class {
|
|
|
1651
1883
|
<div class="uiv-src">${escapeHtml(src)}</div>
|
|
1652
1884
|
<span class="uiv-mech">${st.record.styling.primaryMechanism}</span>
|
|
1653
1885
|
</div>
|
|
1886
|
+
${this.dsIndicatorHtml()}
|
|
1654
1887
|
${this.currentStylesHtml()}
|
|
1655
1888
|
${this.breakpointBarHtml()}
|
|
1656
1889
|
${this.targetHtml(st)}
|
|
@@ -1659,6 +1892,13 @@ var Uivisor = class {
|
|
|
1659
1892
|
`;
|
|
1660
1893
|
this.bindControls();
|
|
1661
1894
|
}
|
|
1895
|
+
/** Small indicator: how many design tokens were detected (or a hint if none). */
|
|
1896
|
+
dsIndicatorHtml() {
|
|
1897
|
+
const ds = this.designSystem();
|
|
1898
|
+
if (ds.source === "none") return "";
|
|
1899
|
+
const cats = Object.keys(ds.byCategory).map((c) => `${ds.byCategory[c].length} ${c}`).join(" \xB7 ");
|
|
1900
|
+
return `<div class="uiv-dsbar" title="${escapeAttr(cats)}">\u25C6 Design system \xB7 ${ds.tokens.length} tokens detected</div>`;
|
|
1901
|
+
}
|
|
1662
1902
|
/** Read-only readout of the element's actual current styles — so you don't guess. */
|
|
1663
1903
|
currentStylesHtml() {
|
|
1664
1904
|
const el = this.selected;
|
|
@@ -1864,6 +2104,25 @@ var Uivisor = class {
|
|
|
1864
2104
|
const units = c.units.map((u) => `<option value="${u}"${u === d.unit ? " selected" : ""}>${UNIT_LABELS[u] ?? u}</option>`).join("");
|
|
1865
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>`;
|
|
1866
2106
|
}
|
|
2107
|
+
/** A design-token picker row for a property, shown only when the project exposes
|
|
2108
|
+
* tokens for that category. Picking a token applies its value + tags the prompt. */
|
|
2109
|
+
tokenRowHtml(css, label) {
|
|
2110
|
+
const cat = this.dsCategoryFor(css);
|
|
2111
|
+
if (!cat) return "";
|
|
2112
|
+
const ds = this.designSystem();
|
|
2113
|
+
const list = ds.byCategory[cat];
|
|
2114
|
+
if (!list || !list.length) return "";
|
|
2115
|
+
const target = cat === "font-size" ? { px: this.currentPx(css) ?? void 0 } : { value: this.computedVal(css) };
|
|
2116
|
+
const near = nearestToken(ds, cat, target);
|
|
2117
|
+
const edited = this.st()?.record.changes.some(
|
|
2118
|
+
(c) => c.property === css && c.after.designToken
|
|
2119
|
+
);
|
|
2120
|
+
const head = `<option value="">${near && !near.exact ? `\u2248 ${escapeHtml(near.token.name)} \xB7 pick token` : "\u2014 pick token \u2014"}</option>`;
|
|
2121
|
+
const opts = list.map(
|
|
2122
|
+
(t) => `<option value="${escapeAttr(t.cssVar)}"${near?.exact && near.token.cssVar === t.cssVar ? " selected" : ""}>${escapeHtml(`${t.name} \xB7 ${t.value}`)}</option>`
|
|
2123
|
+
).join("");
|
|
2124
|
+
return `<div class="uiv-ctl"><span class="clabel uiv-tlabel">${label}</span><div class="cfield"><select class="uiv-sel uiv-tokensel${edited ? " changed" : ""}" data-css="${css}">${head}${opts}</select></div><span></span></div>`;
|
|
2125
|
+
}
|
|
1867
2126
|
controlRow(c) {
|
|
1868
2127
|
if (c.kind === "box") {
|
|
1869
2128
|
const cssList = c.sides.map((s) => s.css);
|
|
@@ -1888,7 +2147,7 @@ var Uivisor = class {
|
|
|
1888
2147
|
}
|
|
1889
2148
|
if (c.kind === "len") {
|
|
1890
2149
|
const v = this.liveNum(c.css);
|
|
1891
|
-
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
|
|
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");
|
|
1892
2151
|
}
|
|
1893
2152
|
if (c.kind === "dim") {
|
|
1894
2153
|
return this.dimField(c);
|
|
@@ -1900,7 +2159,7 @@ var Uivisor = class {
|
|
|
1900
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>`;
|
|
1901
2160
|
}
|
|
1902
2161
|
const val = toHexInput(this.liveVal(c.css));
|
|
1903
|
-
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
|
|
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");
|
|
1904
2163
|
}
|
|
1905
2164
|
bindControls() {
|
|
1906
2165
|
const root = this.root;
|
|
@@ -1942,7 +2201,7 @@ var Uivisor = class {
|
|
|
1942
2201
|
this.commitValue([css], input.value);
|
|
1943
2202
|
});
|
|
1944
2203
|
});
|
|
1945
|
-
root.querySelectorAll(".uiv-sel").forEach((node) => {
|
|
2204
|
+
root.querySelectorAll(".uiv-sel:not(.uiv-tokensel)").forEach((node) => {
|
|
1946
2205
|
const sel = node;
|
|
1947
2206
|
const css = sel.getAttribute("data-css");
|
|
1948
2207
|
sel.addEventListener("change", () => {
|
|
@@ -1950,6 +2209,15 @@ var Uivisor = class {
|
|
|
1950
2209
|
this.commitValue([css], sel.value);
|
|
1951
2210
|
});
|
|
1952
2211
|
});
|
|
2212
|
+
root.querySelectorAll(".uiv-tokensel").forEach((node) => {
|
|
2213
|
+
const sel = node;
|
|
2214
|
+
const css = sel.getAttribute("data-css");
|
|
2215
|
+
sel.addEventListener("change", () => {
|
|
2216
|
+
if (!sel.value) return;
|
|
2217
|
+
const token = this.designSystem().tokens.find((t) => t.cssVar === sel.value);
|
|
2218
|
+
if (token) this.applyToken(css, token);
|
|
2219
|
+
});
|
|
2220
|
+
});
|
|
1953
2221
|
root.querySelectorAll(".uiv-expand").forEach((node) => {
|
|
1954
2222
|
const btn = node;
|
|
1955
2223
|
const key = btn.getAttribute("data-key");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uivisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|