@bitovi/vybit 0.4.6 → 0.4.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.
@@ -1331,249 +1331,25 @@
1331
1331
  }
1332
1332
  return bestResult;
1333
1333
  }
1334
-
1335
- // overlay/src/class-parser.ts
1336
- function deriveValueType(themeKey) {
1337
- if (themeKey === "colors") return "color";
1338
- if (themeKey !== null) return "scalar";
1339
- return "enum";
1340
- }
1341
- var PREFIX_MAP = [
1342
- // Spacing
1343
- { prefix: "px-", category: "spacing", themeKey: "spacing" },
1344
- { prefix: "py-", category: "spacing", themeKey: "spacing" },
1345
- { prefix: "pt-", category: "spacing", themeKey: "spacing" },
1346
- { prefix: "pr-", category: "spacing", themeKey: "spacing" },
1347
- { prefix: "pb-", category: "spacing", themeKey: "spacing" },
1348
- { prefix: "pl-", category: "spacing", themeKey: "spacing" },
1349
- { prefix: "ps-", category: "spacing", themeKey: "spacing" },
1350
- { prefix: "pe-", category: "spacing", themeKey: "spacing" },
1351
- { prefix: "p-", category: "spacing", themeKey: "spacing" },
1352
- { prefix: "mx-", category: "spacing", themeKey: "spacing" },
1353
- { prefix: "my-", category: "spacing", themeKey: "spacing" },
1354
- { prefix: "mt-", category: "spacing", themeKey: "spacing" },
1355
- { prefix: "mr-", category: "spacing", themeKey: "spacing" },
1356
- { prefix: "mb-", category: "spacing", themeKey: "spacing" },
1357
- { prefix: "ml-", category: "spacing", themeKey: "spacing" },
1358
- { prefix: "ms-", category: "spacing", themeKey: "spacing" },
1359
- { prefix: "me-", category: "spacing", themeKey: "spacing" },
1360
- { prefix: "m-", category: "spacing", themeKey: "spacing" },
1361
- { prefix: "gap-x-", category: "spacing", themeKey: "spacing" },
1362
- { prefix: "gap-y-", category: "spacing", themeKey: "spacing" },
1363
- { prefix: "gap-", category: "spacing", themeKey: "spacing" },
1364
- { prefix: "space-x-", category: "spacing", themeKey: "spacing" },
1365
- { prefix: "space-y-", category: "spacing", themeKey: "spacing" },
1366
- // Sizing
1367
- { prefix: "min-w-", category: "sizing", themeKey: "spacing" },
1368
- { prefix: "max-w-", category: "sizing", themeKey: "spacing" },
1369
- { prefix: "min-h-", category: "sizing", themeKey: "spacing" },
1370
- { prefix: "max-h-", category: "sizing", themeKey: "spacing" },
1371
- { prefix: "size-", category: "sizing", themeKey: "spacing" },
1372
- { prefix: "w-", category: "sizing", themeKey: "spacing" },
1373
- { prefix: "h-", category: "sizing", themeKey: "spacing" },
1374
- // Color
1375
- { prefix: "bg-", category: "color", themeKey: "colors" },
1376
- { prefix: "ring-", category: "color", themeKey: "colors" },
1377
- { prefix: "outline-", category: "color", themeKey: "colors" },
1378
- { prefix: "fill-", category: "color", themeKey: "colors" },
1379
- { prefix: "stroke-", category: "color", themeKey: "colors" },
1380
- { prefix: "decoration-", category: "color", themeKey: "colors" },
1381
- // Typography
1382
- { prefix: "font-", category: "typography", themeKey: "fontWeight" },
1383
- { prefix: "leading-", category: "typography", themeKey: "lineHeight" },
1384
- { prefix: "tracking-", category: "typography", themeKey: "letterSpacing" },
1385
- // Borders
1386
- { prefix: "rounded-tl-", category: "borders", themeKey: "borderRadius" },
1387
- { prefix: "rounded-tr-", category: "borders", themeKey: "borderRadius" },
1388
- { prefix: "rounded-br-", category: "borders", themeKey: "borderRadius" },
1389
- { prefix: "rounded-bl-", category: "borders", themeKey: "borderRadius" },
1390
- { prefix: "rounded-t-", category: "borders", themeKey: "borderRadius" },
1391
- { prefix: "rounded-r-", category: "borders", themeKey: "borderRadius" },
1392
- { prefix: "rounded-b-", category: "borders", themeKey: "borderRadius" },
1393
- { prefix: "rounded-l-", category: "borders", themeKey: "borderRadius" },
1394
- { prefix: "rounded-", category: "borders", themeKey: "borderRadius" },
1395
- { prefix: "border-t-", category: "borders", themeKey: "borderWidth" },
1396
- { prefix: "border-r-", category: "borders", themeKey: "borderWidth" },
1397
- { prefix: "border-b-", category: "borders", themeKey: "borderWidth" },
1398
- { prefix: "border-l-", category: "borders", themeKey: "borderWidth" },
1399
- // Effects
1400
- { prefix: "opacity-", category: "effects", themeKey: null },
1401
- { prefix: "shadow-", category: "effects", themeKey: null },
1402
- // Layout
1403
- { prefix: "inset-", category: "layout", themeKey: "spacing" },
1404
- { prefix: "top-", category: "layout", themeKey: "spacing" },
1405
- { prefix: "right-", category: "layout", themeKey: "spacing" },
1406
- { prefix: "bottom-", category: "layout", themeKey: "spacing" },
1407
- { prefix: "left-", category: "layout", themeKey: "spacing" },
1408
- { prefix: "z-", category: "layout", themeKey: null },
1409
- // Flexbox & Grid
1410
- { prefix: "basis-", category: "flexbox", themeKey: "spacing" },
1411
- { prefix: "grid-cols-", category: "flexbox", themeKey: null },
1412
- { prefix: "grid-rows-", category: "flexbox", themeKey: null },
1413
- { prefix: "col-span-", category: "flexbox", themeKey: null },
1414
- { prefix: "row-span-", category: "flexbox", themeKey: null },
1415
- { prefix: "order-", category: "flexbox", themeKey: null }
1416
- ].sort((a, b) => b.prefix.length - a.prefix.length);
1417
- var BORDER_WIDTH_VALUES = /* @__PURE__ */ new Set(["0", "2", "4", "8"]);
1418
- var BORDER_STYLE_KEYWORDS = /* @__PURE__ */ new Set(["solid", "dashed", "dotted", "double", "hidden", "none"]);
1419
- var FONT_SIZE_TOKENS = /* @__PURE__ */ new Set(["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"]);
1420
- var TEXT_ALIGN_KEYWORDS = /* @__PURE__ */ new Set(["left", "center", "right", "justify", "start", "end"]);
1421
- var EXACT_MATCH_MAP = {
1422
- // Borders
1423
- "rounded": { category: "borders", themeKey: "borderRadius" },
1424
- "border": { category: "borders", themeKey: "borderWidth" },
1425
- // Effects
1426
- "shadow": { category: "effects", themeKey: null },
1427
- // Typography
1428
- "underline": { category: "typography", themeKey: null },
1429
- "overline": { category: "typography", themeKey: null },
1430
- "line-through": { category: "typography", themeKey: null },
1431
- "no-underline": { category: "typography", themeKey: null },
1432
- "uppercase": { category: "typography", themeKey: null },
1433
- "lowercase": { category: "typography", themeKey: null },
1434
- "capitalize": { category: "typography", themeKey: null },
1435
- "normal-case": { category: "typography", themeKey: null },
1436
- "truncate": { category: "typography", themeKey: null },
1437
- "italic": { category: "typography", themeKey: null },
1438
- "not-italic": { category: "typography", themeKey: null },
1439
- // Layout
1440
- "block": { category: "layout", themeKey: null },
1441
- "inline-block": { category: "layout", themeKey: null },
1442
- "inline": { category: "layout", themeKey: null },
1443
- "flex": { category: "layout", themeKey: null },
1444
- "inline-flex": { category: "layout", themeKey: null },
1445
- "grid": { category: "layout", themeKey: null },
1446
- "inline-grid": { category: "layout", themeKey: null },
1447
- "hidden": { category: "layout", themeKey: null },
1448
- "table": { category: "layout", themeKey: null },
1449
- "contents": { category: "layout", themeKey: null },
1450
- "static": { category: "layout", themeKey: null },
1451
- "fixed": { category: "layout", themeKey: null },
1452
- "absolute": { category: "layout", themeKey: null },
1453
- "relative": { category: "layout", themeKey: null },
1454
- "sticky": { category: "layout", themeKey: null },
1455
- // Flexbox
1456
- "flex-row": { category: "flexbox", themeKey: null },
1457
- "flex-row-reverse": { category: "flexbox", themeKey: null },
1458
- "flex-col": { category: "flexbox", themeKey: null },
1459
- "flex-col-reverse": { category: "flexbox", themeKey: null },
1460
- "flex-wrap": { category: "flexbox", themeKey: null },
1461
- "flex-wrap-reverse": { category: "flexbox", themeKey: null },
1462
- "flex-nowrap": { category: "flexbox", themeKey: null },
1463
- "flex-1": { category: "flexbox", themeKey: null },
1464
- "flex-auto": { category: "flexbox", themeKey: null },
1465
- "flex-initial": { category: "flexbox", themeKey: null },
1466
- "flex-none": { category: "flexbox", themeKey: null },
1467
- "grow": { category: "flexbox", themeKey: null },
1468
- "grow-0": { category: "flexbox", themeKey: null },
1469
- "shrink": { category: "flexbox", themeKey: null },
1470
- "shrink-0": { category: "flexbox", themeKey: null },
1471
- "justify-start": { category: "flexbox", themeKey: null },
1472
- "justify-end": { category: "flexbox", themeKey: null },
1473
- "justify-center": { category: "flexbox", themeKey: null },
1474
- "justify-between": { category: "flexbox", themeKey: null },
1475
- "justify-around": { category: "flexbox", themeKey: null },
1476
- "justify-evenly": { category: "flexbox", themeKey: null },
1477
- "justify-stretch": { category: "flexbox", themeKey: null },
1478
- "items-start": { category: "flexbox", themeKey: null },
1479
- "items-end": { category: "flexbox", themeKey: null },
1480
- "items-center": { category: "flexbox", themeKey: null },
1481
- "items-baseline": { category: "flexbox", themeKey: null },
1482
- "items-stretch": { category: "flexbox", themeKey: null },
1483
- "self-auto": { category: "flexbox", themeKey: null },
1484
- "self-start": { category: "flexbox", themeKey: null },
1485
- "self-end": { category: "flexbox", themeKey: null },
1486
- "self-center": { category: "flexbox", themeKey: null },
1487
- "self-stretch": { category: "flexbox", themeKey: null },
1488
- "self-baseline": { category: "flexbox", themeKey: null }
1489
- };
1490
- var VARIANT_PREFIXES = /^(sm:|md:|lg:|xl:|2xl:|hover:|focus:|active:|disabled:|visited:|first:|last:|odd:|even:|group-hover:|focus-within:|focus-visible:|dark:|motion-safe:|motion-reduce:)+/;
1491
- function hasVariantPrefix(cls) {
1492
- return VARIANT_PREFIXES.test(cls);
1493
- }
1494
- function parseTextClass(value) {
1495
- if (FONT_SIZE_TOKENS.has(value)) {
1496
- return { category: "typography", valueType: "scalar", prefix: "text-", value, fullClass: `text-${value}`, themeKey: "fontSize" };
1497
- }
1498
- if (TEXT_ALIGN_KEYWORDS.has(value)) {
1499
- return { category: "typography", valueType: "enum", prefix: "text-", value, fullClass: `text-${value}`, themeKey: null };
1500
- }
1501
- return { category: "color", valueType: "color", prefix: "text-", value, fullClass: `text-${value}`, themeKey: "colors" };
1502
- }
1503
- function parseBorderClass(value) {
1504
- if (BORDER_WIDTH_VALUES.has(value)) {
1505
- return { category: "borders", valueType: "scalar", prefix: "border-", value, fullClass: `border-${value}`, themeKey: "borderWidth" };
1506
- }
1507
- if (BORDER_STYLE_KEYWORDS.has(value)) {
1508
- return { category: "borders", valueType: "enum", prefix: "border-", value, fullClass: `border-${value}`, themeKey: null };
1509
- }
1510
- return { category: "color", valueType: "color", prefix: "border-", value, fullClass: `border-${value}`, themeKey: "colors" };
1511
- }
1512
- function parseClasses(classString) {
1513
- const results = [];
1514
- const classes = classString.trim().split(/\s+/).filter(Boolean);
1515
- for (const cls of classes) {
1516
- if (hasVariantPrefix(cls)) continue;
1517
- const exact = EXACT_MATCH_MAP[cls];
1518
- if (exact) {
1519
- results.push({
1520
- category: exact.category,
1521
- valueType: deriveValueType(exact.themeKey),
1522
- prefix: cls,
1523
- value: "",
1524
- fullClass: cls,
1525
- themeKey: exact.themeKey
1526
- });
1527
- continue;
1528
- }
1529
- if (cls.startsWith("text-")) {
1530
- const value = cls.slice(5);
1531
- const parsed = parseTextClass(value);
1532
- if (parsed) results.push(parsed);
1533
- continue;
1534
- }
1535
- if (cls.startsWith("border-")) {
1536
- const value = cls.slice(7);
1537
- let longerMatch = false;
1538
- for (const entry of PREFIX_MAP) {
1539
- if (entry.prefix !== "border-" && cls.startsWith(entry.prefix)) {
1540
- longerMatch = true;
1541
- results.push({
1542
- category: entry.category,
1543
- valueType: deriveValueType(entry.themeKey),
1544
- prefix: entry.prefix,
1545
- value: cls.slice(entry.prefix.length),
1546
- fullClass: cls,
1547
- themeKey: entry.themeKey
1548
- });
1549
- break;
1550
- }
1551
- }
1552
- if (!longerMatch) {
1553
- const parsed = parseBorderClass(value);
1554
- if (parsed) results.push(parsed);
1555
- }
1556
- continue;
1557
- }
1558
- let matched = false;
1559
- for (const entry of PREFIX_MAP) {
1560
- if (cls.startsWith(entry.prefix)) {
1561
- results.push({
1562
- category: entry.category,
1563
- valueType: deriveValueType(entry.themeKey),
1564
- prefix: entry.prefix,
1565
- value: cls.slice(entry.prefix.length),
1566
- fullClass: cls,
1567
- themeKey: entry.themeKey
1568
- });
1569
- matched = true;
1570
- break;
1571
- }
1572
- }
1573
- if (!matched) {
1574
- }
1575
- }
1576
- return results;
1334
+ function findDOMEquivalents(el) {
1335
+ const classes = typeof el.className === "string" ? el.className.trim() : "";
1336
+ if (!classes) return [el];
1337
+ const exactMatches = Array.from(
1338
+ document.querySelectorAll(el.tagName.toLowerCase())
1339
+ ).filter((n) => n.className === el.className);
1340
+ if (exactMatches.length >= 2) return exactMatches;
1341
+ const parent = el.parentElement;
1342
+ if (!parent) return [el];
1343
+ const siblings = Array.from(parent.children).filter(
1344
+ (c) => c instanceof HTMLElement && c.tagName === el.tagName
1345
+ );
1346
+ if (siblings.length < 2) return [el];
1347
+ const majorityClass = siblings.map((n) => n.className).sort(
1348
+ (a, b) => siblings.filter((n) => n.className === b).length - siblings.filter((n) => n.className === a).length
1349
+ )[0];
1350
+ const outliers = siblings.filter((n) => n.className !== majorityClass);
1351
+ if (outliers.length > 1) return [el];
1352
+ return siblings.filter((n) => n.className === majorityClass);
1577
1353
  }
1578
1354
 
1579
1355
  // overlay/src/context.ts
@@ -2497,16 +2273,9 @@ ${pad}</${tag}>`;
2497
2273
  return;
2498
2274
  }
2499
2275
  const fiber = getFiber(target);
2500
- if (!fiber) {
2501
- clearHoverPreview();
2502
- return;
2503
- }
2504
- const boundary = findComponentBoundary(fiber);
2505
- if (!boundary) {
2506
- clearHoverPreview();
2507
- return;
2508
- }
2509
- showHoverPreview(target, boundary.componentName);
2276
+ const boundary = fiber ? findComponentBoundary(fiber) : null;
2277
+ const label = boundary?.componentName ?? target.tagName.toLowerCase();
2278
+ showHoverPreview(target, label);
2510
2279
  }
2511
2280
  var PENCIL_SVG = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2512
2281
  <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
@@ -2795,26 +2564,27 @@ ${pad}</${tag}>`;
2795
2564
  e.stopPropagation();
2796
2565
  const target = e.target;
2797
2566
  const fiber = getFiber(target);
2798
- if (!fiber) {
2799
- showToast("Could not detect a React component for this element.");
2800
- return;
2801
- }
2802
- const boundary = findComponentBoundary(fiber);
2803
- if (!boundary) {
2804
- showToast("Could not detect a React component for this element.");
2805
- return;
2806
- }
2807
- const rootFiber = getRootFiber();
2808
- if (!rootFiber) {
2809
- showToast("Could not find React root.");
2810
- return;
2811
- }
2812
- const instances = findAllInstances(rootFiber, boundary.componentType);
2813
- const path = getChildPath(boundary.componentFiber, fiber);
2567
+ const boundary = fiber ? findComponentBoundary(fiber) : null;
2568
+ const hasFiber = fiber !== null && boundary !== null;
2814
2569
  const newNodes = [];
2815
- for (const inst of instances) {
2816
- const node = resolvePathToDOM(inst, path);
2817
- if (node) newNodes.push(node);
2570
+ let componentName;
2571
+ if (hasFiber) {
2572
+ const rootFiber = getRootFiber();
2573
+ if (!rootFiber) {
2574
+ showToast("Could not find React root.");
2575
+ return;
2576
+ }
2577
+ const instances = findAllInstances(rootFiber, boundary.componentType);
2578
+ const path = getChildPath(boundary.componentFiber, fiber);
2579
+ for (const inst of instances) {
2580
+ const node = resolvePathToDOM(inst, path);
2581
+ if (node) newNodes.push(node);
2582
+ }
2583
+ componentName = boundary.componentName;
2584
+ } else {
2585
+ const targetEl2 = target;
2586
+ newNodes.push(...findDOMEquivalents(targetEl2));
2587
+ componentName = targetEl2.tagName.toLowerCase();
2818
2588
  }
2819
2589
  if (addingMode && currentEquivalentNodes.length > 0) {
2820
2590
  addingMode = false;
@@ -2827,7 +2597,7 @@ ${pad}</${tag}>`;
2827
2597
  currentEquivalentNodes = merged;
2828
2598
  selectedInstanceIndices = /* @__PURE__ */ new Set();
2829
2599
  if (currentTargetEl) showDrawButton(currentTargetEl);
2830
- sendTo("panel", { type: "ELEMENT_SELECTED", componentName: currentBoundary?.componentName ?? boundary.componentName, instanceCount: merged.length, classes: currentTargetEl?.className ?? "", tailwindConfig: await fetchTailwindConfig() });
2600
+ sendTo("panel", { type: "ELEMENT_SELECTED", componentName: currentBoundary?.componentName ?? componentName, instanceCount: merged.length, classes: currentTargetEl?.className ?? "", tailwindConfig: await fetchTailwindConfig() });
2831
2601
  return;
2832
2602
  }
2833
2603
  clearHighlights();
@@ -2836,7 +2606,7 @@ ${pad}</${tag}>`;
2836
2606
  equivalentNodes.push(node);
2837
2607
  highlightElement(node);
2838
2608
  }
2839
- if (equivalentNodes.length <= 1) {
2609
+ if (hasFiber && equivalentNodes.length <= 1) {
2840
2610
  const repeated = findInlineRepeatedNodes(fiber, boundary.componentFiber);
2841
2611
  if (repeated.length > 0) {
2842
2612
  clearHighlights();
@@ -2847,24 +2617,32 @@ ${pad}</${tag}>`;
2847
2617
  }
2848
2618
  }
2849
2619
  }
2850
- console.log(`[overlay] ${boundary.componentName} \u2014 ${instances.length} instances, ${equivalentNodes.length} highlighted`);
2620
+ console.log(`[overlay] ${componentName} \u2014 ${equivalentNodes.length} highlighted`);
2851
2621
  const config = await fetchTailwindConfig();
2852
2622
  const targetEl = target;
2853
2623
  const classString = targetEl.className;
2854
2624
  if (typeof classString !== "string") return;
2855
- const parsedClasses = parseClasses(classString);
2856
- if (parsedClasses.length === 0) return;
2857
2625
  currentEquivalentNodes = equivalentNodes;
2858
2626
  currentTargetEl = targetEl;
2859
- currentBoundary = { componentName: boundary.componentName };
2627
+ currentBoundary = { componentName };
2860
2628
  selectedInstanceIndices = /* @__PURE__ */ new Set();
2861
- currentInstances = instances.map((inst, i) => {
2862
- const domNode = inst.stateNode instanceof HTMLElement ? inst.stateNode : null;
2863
- const label = domNode ? (domNode.innerText || "").trim().slice(0, 40) || `#${i + 1}` : `#${i + 1}`;
2864
- const parentFiber = inst.return;
2865
- const parent = parentFiber?.type?.name ?? "";
2866
- return { index: i, label, parent };
2867
- });
2629
+ if (hasFiber) {
2630
+ const rootFiber = getRootFiber();
2631
+ const instances = rootFiber ? findAllInstances(rootFiber, boundary.componentType) : [];
2632
+ currentInstances = instances.map((inst, i) => {
2633
+ const domNode = inst.stateNode instanceof HTMLElement ? inst.stateNode : null;
2634
+ const label = domNode ? (domNode.innerText || "").trim().slice(0, 40) || `#${i + 1}` : `#${i + 1}`;
2635
+ const parentFiber = inst.return;
2636
+ const parent = parentFiber?.type?.name ?? "";
2637
+ return { index: i, label, parent };
2638
+ });
2639
+ } else {
2640
+ currentInstances = equivalentNodes.map((node, i) => ({
2641
+ index: i,
2642
+ label: (node.innerText || "").trim().slice(0, 40) || `#${i + 1}`,
2643
+ parent: node.parentElement?.tagName.toLowerCase() ?? ""
2644
+ }));
2645
+ }
2868
2646
  clearHoverPreview();
2869
2647
  setSelectMode(false);
2870
2648
  showDrawButton(targetEl);
@@ -2874,7 +2652,7 @@ ${pad}</${tag}>`;
2874
2652
  }
2875
2653
  sendTo("panel", {
2876
2654
  type: "ELEMENT_SELECTED",
2877
- componentName: boundary.componentName,
2655
+ componentName,
2878
2656
  instanceCount: equivalentNodes.length,
2879
2657
  classes: classString,
2880
2658
  tailwindConfig: config
@@ -3211,6 +2989,12 @@ ${pad}</${tag}>`;
3211
2989
  currentEquivalentNodes.forEach((n) => highlightElement(n));
3212
2990
  }
3213
2991
  });
2992
+ window.addEventListener("scroll", () => {
2993
+ if (currentEquivalentNodes.length > 0) {
2994
+ shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
2995
+ currentEquivalentNodes.forEach((n) => highlightElement(n));
2996
+ }
2997
+ }, { capture: true, passive: true });
3214
2998
  window.addEventListener("overlay-ws-connected", () => {
3215
2999
  if (wasConnected) {
3216
3000
  showToast("Reconnected");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitovi/vybit",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Browser overlay + inspector panel + MCP server for visually editing Tailwind CSS classes on a running React app",
5
5
  "keywords": [
6
6
  "tailwind",