@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 +6 -2
- package/overlay/dist/overlay.js +89 -3
- package/package.json +1 -1
- package/panel/dist/assets/{DesignMode-DiJSKaJo.js → DesignMode-DTled2fb.js} +37 -37
- package/panel/dist/assets/index-BT9whNBX.js +62 -0
- package/panel/dist/assets/index-rvhMGLF5.css +1 -0
- package/panel/dist/index.html +2 -2
- package/server/app.ts +3 -3
- package/server/ghost-cache.ts +5 -11
- package/shared/css-utils.ts +21 -0
- package/shared/types.ts +4 -0
- package/panel/dist/assets/index--Jf_7IdL.js +0 -61
- package/panel/dist/assets/index-DN-H7d0q.css +0 -1
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
|
-
|
|
5
|
+
[Watch a video going over its features](https://www.youtube.com/watch?v=dE2rYcyC2xk)
|
|
6
|
+
|
|
7
|
+
[](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 |
|
|
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:
|
package/overlay/dist/overlay.js
CHANGED
|
@@ -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();
|