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.
@@ -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
- const major = nextMajor();
104
- if (major === 0 || major >= 15) {
105
+ if (modern) {
105
106
  out.turbopack = {
106
107
  ...nextConfig.turbopack,
107
108
  rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
@@ -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
- const major = nextMajor();
76
- if (major === 0 || major >= 15) {
77
+ if (modern) {
77
78
  out.turbopack = {
78
79
  ...nextConfig.turbopack,
79
80
  rules: { ...nextConfig.turbopack?.rules ?? {}, ...rules }
@@ -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
- const suggestion = c.after.token ? ` \u2192 \`${variant(bp, c.after.token)}\`` : "";
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
- if (!prev || key(prev) !== key(next)) this.renderBody();
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",
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",