@bitovi/vybit 0.13.3 → 0.13.4

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/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  Change designs, draw mockups, provide suggestions, and report bugs __in your browser__ and send them to your favorite coding agent (Claude, Cursor, Copilot, etc) to be implemented. VyBit works with React or Angular apps built with Tailwind v3 or v4.
4
4
 
5
- <img width="1453" height="903" alt="Cursor_and_Carton_Case_Management" src="https://github.com/user-attachments/assets/59b8e280-a827-4fa0-95e3-6c350afacbc9" />
5
+ [Watch a video going over its features](https://www.youtube.com/watch?v=dE2rYcyC2xk)
6
+
7
+ [![Watch the video](https://github.com/user-attachments/assets/99961c66-e2c9-48e3-9e7d-c23d6eda37ca)](https://www.youtube.com/watch?v=dE2rYcyC2xk)
8
+
6
9
 
7
10
  `VyBit` changes how you can design and build an app or website. Instead of building your design system and page designs in Sketch or Figma and then implementing it in code, you:
8
11
 
@@ -12,12 +15,13 @@ Change designs, draw mockups, provide suggestions, and report bugs __in your bro
12
15
  | 2 | __Use VyBit to fine-tune your design system in Storybook__ - Adjust colors, spacing, shadows, layout and more | <img alt="image" src="https://github.com/user-attachments/assets/79ca04be-db8f-458f-8632-87cc040875db" /> |
13
16
  | 3a | __Use VyBit to design features__ - drop your design system components into pages | <img width="1481" height="922" alt="image" src="https://github.com/user-attachments/assets/415acdb7-102a-4c31-910b-10536c59ee4a" /> |
14
17
  | 3b | __Use VyBit to design features__ - sketch a feature with the design canvas | <img width="1482" height="924" alt="image" src="https://github.com/user-attachments/assets/924e9733-baf6-4492-b9da-05fd27c2df93" /> |
15
- | 3c | __Use VyBit to report bugs__ - send recent errors, console.logs, DOM snapshots, and events | Image Coming Soon |
18
+ | 3c | __Use VyBit to report bugs__ - send recent errors, console.logs, DOM snapshots, and events | <img width="1482" height="1080" alt="Untitled Project" src="https://github.com/user-attachments/assets/7754ccb4-0d4c-4f02-8ea3-dca45943fd9a" /> |
16
19
  | 4 | Add text or voice messages for extra context | <img width="376" height="261" alt="image" src="https://github.com/user-attachments/assets/546ea987-a0ad-4809-85c6-52fb91fb987e" /> |
17
20
 
18
21
  Plus, VyBit always knows what page, components, and elements you're editing, making it easier for agents to know exactly what you want!
19
22
 
20
23
 
24
+
21
25
  ## Installation
22
26
 
23
27
  To use VyBit:
@@ -1041,6 +1041,7 @@ ${pad}</${tag}>`;
1041
1041
  componentName: msg.componentName,
1042
1042
  storyId: msg.storyId,
1043
1043
  ghostHtml: msg.ghostHtml,
1044
+ ghostCss: msg.ghostCss ?? "",
1044
1045
  componentPath: msg.componentPath ?? "",
1045
1046
  componentArgs: msg.args ?? {}
1046
1047
  },
@@ -1048,6 +1049,71 @@ ${pad}</${tag}>`;
1048
1049
  `Place: ${msg.componentName}`
1049
1050
  );
1050
1051
  }
1052
+ function placeAtLockedInsert(msg) {
1053
+ if (!locked.target || !locked.position) return false;
1054
+ const target = locked.target;
1055
+ const position = locked.position;
1056
+ const template = document.createElement("template");
1057
+ template.innerHTML = msg.ghostHtml.trim();
1058
+ const inserted = template.content.firstElementChild;
1059
+ if (!inserted) return false;
1060
+ inserted.dataset.twDroppedComponent = msg.componentName;
1061
+ switch (position) {
1062
+ case "before":
1063
+ target.insertAdjacentElement("beforebegin", inserted);
1064
+ break;
1065
+ case "after":
1066
+ target.insertAdjacentElement("afterend", inserted);
1067
+ break;
1068
+ case "first-child":
1069
+ target.insertAdjacentElement("afterbegin", inserted);
1070
+ break;
1071
+ case "last-child":
1072
+ target.appendChild(inserted);
1073
+ break;
1074
+ }
1075
+ if (msg.ghostCss) injectGhostCss(msg.componentName, msg.ghostCss);
1076
+ const targetSelector = buildSelector(target);
1077
+ const isGhostTarget = !!target.dataset.twDroppedComponent;
1078
+ const ghostTargetPatchId = target.dataset.twDroppedPatchId;
1079
+ const ghostTargetName = target.dataset.twDroppedComponent;
1080
+ const ghostAncestor = !isGhostTarget ? findGhostAncestor(target) : null;
1081
+ const effectiveGhostName = isGhostTarget ? ghostTargetName : ghostAncestor?.dataset.twDroppedComponent;
1082
+ const effectiveGhostPatchId = isGhostTarget ? ghostTargetPatchId : ghostAncestor?.dataset.twDroppedPatchId;
1083
+ const context = effectiveGhostName ? `Place "${msg.componentName}" ${position} the <${effectiveGhostName} /> component (pending insertion from an earlier drop)` : buildContext(target, "", "", /* @__PURE__ */ new Map());
1084
+ let parentComponent;
1085
+ const fiber = getFiber(target);
1086
+ if (fiber) {
1087
+ const boundary = findComponentBoundary(fiber);
1088
+ if (boundary) parentComponent = { name: boundary.componentName };
1089
+ }
1090
+ const patch = {
1091
+ id: crypto.randomUUID(),
1092
+ kind: "component-drop",
1093
+ elementKey: targetSelector,
1094
+ status: "staged",
1095
+ originalClass: "",
1096
+ newClass: "",
1097
+ property: "component-drop",
1098
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1099
+ component: { name: msg.componentName },
1100
+ target: isGhostTarget ? { tag: ghostTargetName?.toLowerCase() ?? "unknown", classes: "", innerText: "" } : { tag: target.tagName.toLowerCase(), classes: target.className, innerText: target.innerText.slice(0, 100) },
1101
+ ghostHtml: msg.ghostHtml,
1102
+ ghostCss: msg.ghostCss || void 0,
1103
+ componentStoryId: msg.storyId,
1104
+ componentPath: msg.componentPath || void 0,
1105
+ componentArgs: Object.keys(msg.args ?? {}).length > 0 ? msg.args : void 0,
1106
+ parentComponent,
1107
+ insertMode: position,
1108
+ context,
1109
+ ...effectiveGhostPatchId ? { targetPatchId: effectiveGhostPatchId, targetComponentName: effectiveGhostName } : {}
1110
+ };
1111
+ inserted.dataset.twDroppedPatchId = patch.id;
1112
+ send({ type: "COMPONENT_DROPPED", patch });
1113
+ sendTo("panel", { type: "COMPONENT_DISARMED" });
1114
+ clearLockedInsert();
1115
+ return true;
1116
+ }
1051
1117
  function cancelInsert() {
1052
1118
  cleanup();
1053
1119
  }
@@ -1059,6 +1125,9 @@ ${pad}</${tag}>`;
1059
1125
  inserted.dataset.twDroppedComponent = msg.componentName;
1060
1126
  target.insertAdjacentElement("beforebegin", inserted);
1061
1127
  target.style.display = "none";
1128
+ if (msg.ghostCss) {
1129
+ injectGhostCss(msg.componentName, msg.ghostCss);
1130
+ }
1062
1131
  const targetSelector = buildSelector(target);
1063
1132
  const isGhostTarget = !!target.dataset.twDroppedComponent;
1064
1133
  const ghostTargetPatchId = target.dataset.twDroppedPatchId;
@@ -1089,6 +1158,7 @@ ${pad}</${tag}>`;
1089
1158
  innerText: target.innerText.slice(0, 100)
1090
1159
  },
1091
1160
  ghostHtml: msg.ghostHtml,
1161
+ ghostCss: msg.ghostCss || void 0,
1092
1162
  componentStoryId: msg.storyId,
1093
1163
  componentPath: msg.componentPath || void 0,
1094
1164
  componentArgs: Object.keys(msg.args ?? {}).length > 0 ? msg.args : void 0,
@@ -1487,7 +1557,7 @@ ${pad}</${tag}>`;
1487
1557
  e.preventDefault();
1488
1558
  e.stopPropagation();
1489
1559
  if (mode.kind !== "component-insert") return;
1490
- const { componentName: cName, storyId: sId, ghostHtml: gHtml, componentPath: cPath, componentArgs: cArgs } = mode;
1560
+ const { componentName: cName, storyId: sId, ghostHtml: gHtml, ghostCss: gCss, componentPath: cPath, componentArgs: cArgs } = mode;
1491
1561
  const template = document.createElement("template");
1492
1562
  template.innerHTML = gHtml.trim();
1493
1563
  const inserted = template.content.firstElementChild;
@@ -1513,6 +1583,9 @@ ${pad}</${tag}>`;
1513
1583
  target.appendChild(inserted);
1514
1584
  break;
1515
1585
  }
1586
+ if (gCss) {
1587
+ injectGhostCss(cName, gCss);
1588
+ }
1516
1589
  const targetSelector = buildSelector(target);
1517
1590
  const isGhostTarget = !!target.dataset.twDroppedComponent;
1518
1591
  const ghostTargetPatchId = target.dataset.twDroppedPatchId;
@@ -1543,6 +1616,7 @@ ${pad}</${tag}>`;
1543
1616
  innerText: target.innerText.slice(0, 100)
1544
1617
  },
1545
1618
  ghostHtml: gHtml,
1619
+ ghostCss: gCss || void 0,
1546
1620
  componentStoryId: sId,
1547
1621
  componentPath: cPath || void 0,
1548
1622
  componentArgs: Object.keys(cArgs).length > 0 ? cArgs : void 0,
@@ -1594,6 +1668,15 @@ ${pad}</${tag}>`;
1594
1668
  dom.currentTarget = null;
1595
1669
  dom.currentPosition = null;
1596
1670
  }
1671
+ function injectGhostCss(componentName, css2) {
1672
+ const id = `vybit-ghost-css-${componentName}`;
1673
+ const existing = document.getElementById(id);
1674
+ if (existing) existing.remove();
1675
+ const style = document.createElement("style");
1676
+ style.id = id;
1677
+ style.textContent = css2;
1678
+ document.head.appendChild(style);
1679
+ }
1597
1680
 
1598
1681
  // node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs
1599
1682
  var min = Math.min;
@@ -6882,7 +6965,7 @@ ${pad}</${tag}>`;
6882
6965
  req.onsuccess = () => resolve(req.result);
6883
6966
  req.onerror = () => reject(req.error);
6884
6967
  });
6885
- await this.pruneIfNeeded();
6968
+ await this.pruneIfNeeded().catch((err) => console.warn("[SnapshotStore] pruneIfNeeded failed:", err));
6886
6969
  return id;
6887
6970
  }
6888
6971
  /** Get a single snapshot by ID. */
@@ -7898,8 +7981,11 @@ ${pad}</${tag}>`;
7898
7981
  } else {
7899
7982
  armElementSelect(`Replace: ${msg.componentName}`, state.shadowHost, doReplace);
7900
7983
  }
7901
- } else {
7984
+ } else if (!placeAtLockedInsert(msg)) {
7902
7985
  armInsert(msg, state.shadowHost);
7986
+ } else {
7987
+ clearHighlights();
7988
+ clearSelectionState();
7903
7989
  }
7904
7990
  } else if (msg.type === "COMPONENT_DISARM") {
7905
7991
  cancelInsert();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitovi/vybit",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
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",