@bitovi/vybit 0.5.0 → 0.7.0

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.
@@ -1,9 +1,13 @@
1
1
  // Tailwind v4 adapter — uses compile() / build() from the target project's tailwindcss v4.
2
2
 
3
3
  import { readFileSync } from "fs";
4
- import { resolve, dirname } from "path";
5
4
  import { createRequire } from "module";
6
- import type { TailwindAdapter, TailwindThemeSubset } from "./tailwind-adapter.js";
5
+ import { dirname, resolve } from "path";
6
+ import { pathToFileURL } from "url";
7
+ import type {
8
+ TailwindAdapter,
9
+ TailwindThemeSubset,
10
+ } from "./tailwind-adapter.js";
7
11
 
8
12
  // Cached compiler instance (from target project's tailwindcss)
9
13
  let compilerCache: { build: (classes: string[]) => string } | null = null;
@@ -11,17 +15,19 @@ let compilerCache: { build: (classes: string[]) => string } | null = null;
11
15
  /**
12
16
  * Get a Tailwind v4 compile() function from the target project's node_modules.
13
17
  */
14
- async function getCompile(): Promise<(css: string, opts: any) => Promise<{ build: (classes: string[]) => string }>> {
15
- const cwd = process.cwd();
16
- const req = createRequire(resolve(cwd, "package.json"));
17
- const tw = await import(req.resolve("tailwindcss"));
18
- // Handle CJS/ESM interop: compile may be on tw directly or on tw.default
19
- const mod = tw.default ?? tw;
20
- const compile = mod.compile ?? mod.default?.compile;
21
- if (typeof compile !== "function") {
22
- throw new Error("Could not find compile() in target project's tailwindcss");
23
- }
24
- return compile;
18
+ async function getCompile(): Promise<
19
+ (css: string, opts: any) => Promise<{ build: (classes: string[]) => string }>
20
+ > {
21
+ const cwd = process.cwd();
22
+ const req = createRequire(resolve(cwd, "package.json"));
23
+ const tw = await import(pathToFileURL(req.resolve("tailwindcss")).href);
24
+ // Handle CJS/ESM interop: compile may be on tw directly or on tw.default
25
+ const mod = tw.default ?? tw;
26
+ const compile = mod.compile ?? mod.default?.compile;
27
+ if (typeof compile !== "function") {
28
+ throw new Error("Could not find compile() in target project's tailwindcss");
29
+ }
30
+ return compile;
25
31
  }
26
32
 
27
33
  /**
@@ -29,132 +35,239 @@ async function getCompile(): Promise<(css: string, opts: any) => Promise<{ build
29
35
  * Resolves @import "tailwindcss" and other stylesheet imports from the target project.
30
36
  */
31
37
  function makeLoadStylesheet(cwd: string) {
32
- const req = createRequire(resolve(cwd, "package.json"));
33
- return async (id: string, base: string) => {
34
- let resolved: string;
35
- if (id === "tailwindcss") {
36
- resolved = req.resolve("tailwindcss/index.css");
37
- } else {
38
- resolved = req.resolve(id, { paths: [base || cwd] });
39
- }
40
- return {
41
- content: readFileSync(resolved, "utf8"),
42
- base: dirname(resolved),
43
- };
44
- };
38
+ const req = createRequire(resolve(cwd, "package.json"));
39
+ return async (id: string, base: string) => {
40
+ let resolved: string;
41
+ if (id === "tailwindcss") {
42
+ resolved = req.resolve("tailwindcss/index.css");
43
+ } else {
44
+ resolved = req.resolve(id, { paths: [base || cwd] });
45
+ }
46
+ return {
47
+ content: readFileSync(resolved, "utf8"),
48
+ base: dirname(resolved),
49
+ };
50
+ };
45
51
  }
46
52
 
47
53
  /**
48
54
  * Initialize the Tailwind v4 compiler for the target project.
49
55
  */
50
- async function getCompiler(): Promise<{ build: (classes: string[]) => string }> {
51
- if (compilerCache) return compilerCache;
56
+ async function getCompiler(): Promise<{
57
+ build: (classes: string[]) => string;
58
+ }> {
59
+ if (compilerCache) return compilerCache;
52
60
 
53
- const cwd = process.cwd();
54
- const compile = await getCompile();
61
+ const cwd = process.cwd();
62
+ const compile = await getCompile();
55
63
 
56
- const result = await compile('@import "tailwindcss";', {
57
- loadStylesheet: makeLoadStylesheet(cwd),
58
- });
64
+ const result = await compile('@import "tailwindcss";', {
65
+ loadStylesheet: makeLoadStylesheet(cwd),
66
+ });
59
67
 
60
- compilerCache = result;
61
- console.error("[tailwind] Initialized Tailwind v4 compiler from target project");
62
- return result;
68
+ compilerCache = result;
69
+ console.error(
70
+ "[tailwind] Initialized Tailwind v4 compiler from target project",
71
+ );
72
+ return result;
63
73
  }
64
74
 
65
75
  // Classes we probe to extract theme values
66
76
  const HUES = [
67
- "slate", "gray", "zinc", "neutral", "stone",
68
- "red", "orange", "amber", "yellow", "lime",
69
- "green", "emerald", "teal", "cyan", "sky",
70
- "blue", "indigo", "violet", "purple", "fuchsia",
71
- "pink", "rose",
77
+ "slate",
78
+ "gray",
79
+ "zinc",
80
+ "neutral",
81
+ "stone",
82
+ "red",
83
+ "orange",
84
+ "amber",
85
+ "yellow",
86
+ "lime",
87
+ "green",
88
+ "emerald",
89
+ "teal",
90
+ "cyan",
91
+ "sky",
92
+ "blue",
93
+ "indigo",
94
+ "violet",
95
+ "purple",
96
+ "fuchsia",
97
+ "pink",
98
+ "rose",
99
+ ];
100
+ const SHADES = [
101
+ "50",
102
+ "100",
103
+ "200",
104
+ "300",
105
+ "400",
106
+ "500",
107
+ "600",
108
+ "700",
109
+ "800",
110
+ "900",
111
+ "950",
72
112
  ];
73
- const SHADES = ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"];
74
113
  const SPECIAL_COLORS = ["black", "white", "transparent"];
75
114
 
76
115
  const SPACING_KEYS = [
77
- "0", "px", "0.5", "1", "1.5", "2", "2.5", "3", "3.5", "4", "5", "6", "7",
78
- "8", "9", "10", "11", "12", "14", "16", "20", "24", "28", "32", "36", "40",
79
- "44", "48", "52", "56", "60", "64", "72", "80", "96",
116
+ "0",
117
+ "px",
118
+ "0.5",
119
+ "1",
120
+ "1.5",
121
+ "2",
122
+ "2.5",
123
+ "3",
124
+ "3.5",
125
+ "4",
126
+ "5",
127
+ "6",
128
+ "7",
129
+ "8",
130
+ "9",
131
+ "10",
132
+ "11",
133
+ "12",
134
+ "14",
135
+ "16",
136
+ "20",
137
+ "24",
138
+ "28",
139
+ "32",
140
+ "36",
141
+ "40",
142
+ "44",
143
+ "48",
144
+ "52",
145
+ "56",
146
+ "60",
147
+ "64",
148
+ "72",
149
+ "80",
150
+ "96",
80
151
  ];
81
152
 
82
- const FONT_SIZE_KEYS = ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"];
83
- const FONT_WEIGHT_KEYS = ["thin", "extralight", "light", "normal", "medium", "semibold", "bold", "extrabold", "black"];
84
- const BORDER_RADIUS_KEYS = ["none", "sm", "", "md", "lg", "xl", "2xl", "3xl", "full"];
153
+ const FONT_SIZE_KEYS = [
154
+ "xs",
155
+ "sm",
156
+ "base",
157
+ "lg",
158
+ "xl",
159
+ "2xl",
160
+ "3xl",
161
+ "4xl",
162
+ "5xl",
163
+ "6xl",
164
+ "7xl",
165
+ "8xl",
166
+ "9xl",
167
+ ];
168
+ const FONT_WEIGHT_KEYS = [
169
+ "thin",
170
+ "extralight",
171
+ "light",
172
+ "normal",
173
+ "medium",
174
+ "semibold",
175
+ "bold",
176
+ "extrabold",
177
+ "black",
178
+ ];
179
+ const BORDER_RADIUS_KEYS = [
180
+ "none",
181
+ "sm",
182
+ "",
183
+ "md",
184
+ "lg",
185
+ "xl",
186
+ "2xl",
187
+ "3xl",
188
+ "full",
189
+ ];
85
190
 
86
191
  /**
87
192
  * Extract --var definitions from compiled CSS output.
88
193
  */
89
194
  function extractVars(css: string, prefix: string): Map<string, string> {
90
- const vars = new Map<string, string>();
91
- const regex = new RegExp(`^\\s*--${prefix}-([\\w.-]+):\\s*([^;]+);`, "gm");
92
- let match;
93
- while ((match = regex.exec(css)) !== null) {
94
- vars.set(match[1], match[2].trim());
95
- }
96
- return vars;
195
+ const vars = new Map<string, string>();
196
+ const regex = new RegExp(`^\\s*--${prefix}-([\\w.-]+):\\s*([^;]+);`, "gm");
197
+ let match;
198
+ while ((match = regex.exec(css)) !== null) {
199
+ vars.set(match[1], match[2].trim());
200
+ }
201
+ return vars;
97
202
  }
98
203
 
99
204
  export class TailwindV4Adapter implements TailwindAdapter {
100
- readonly version = 4 as const;
101
-
102
- async resolveTailwindConfig(): Promise<TailwindThemeSubset> {
103
- const compiler = await getCompiler();
104
-
105
- // Probe color classes to extract theme variable definitions
106
- const probeClasses: string[] = [];
107
- for (const h of HUES) {
108
- for (const s of SHADES) probeClasses.push(`bg-${h}-${s}`);
109
- }
110
- for (const s of SPECIAL_COLORS) probeClasses.push(`bg-${s}`);
111
-
112
- const css = compiler.build(probeClasses);
113
-
114
- // --- Colors (extracted from CSS custom properties) ---
115
- const colorVars = extractVars(css, "color");
116
- const colors: Record<string, unknown> = {};
117
- for (const [name, value] of colorVars) {
118
- const dashIdx = name.lastIndexOf("-");
119
- if (dashIdx > 0) {
120
- const hue = name.substring(0, dashIdx);
121
- const shade = name.substring(dashIdx + 1);
122
- if (/^\d+$/.test(shade)) {
123
- if (!colors[hue]) colors[hue] = {};
124
- (colors[hue] as Record<string, string>)[shade] = value;
125
- continue;
126
- }
127
- }
128
- colors[name] = value;
129
- }
130
- if (!colors["transparent"]) colors["transparent"] = "transparent";
131
-
132
- // --- Spacing (v4 uses calc(var(--spacing) * N)) ---
133
- const spacing: Record<string, string> = {};
134
- for (const k of SPACING_KEYS) {
135
- spacing[k] = k === "px" ? "1px" : k === "0" ? "0px" : `calc(var(--spacing) * ${k})`;
136
- }
137
-
138
- // --- Font size, weight, border radius (static scales in v4) ---
139
- const fontSize: Record<string, unknown> = {};
140
- for (const k of FONT_SIZE_KEYS) fontSize[k] = k;
141
-
142
- const fontWeight: Record<string, unknown> = {};
143
- for (const k of FONT_WEIGHT_KEYS) fontWeight[k] = k;
144
-
145
- const borderRadius: Record<string, string> = {};
146
- for (const k of BORDER_RADIUS_KEYS) borderRadius[k || "DEFAULT"] = k || "DEFAULT";
147
-
148
- const result: TailwindThemeSubset = { spacing, colors, fontSize, fontWeight, borderRadius };
149
- console.error("[tailwind] v4 resolved theme:", {
150
- colors: Object.keys(colors).length + " entries",
151
- spacing: Object.keys(spacing).length + " entries",
152
- });
153
- return result;
154
- }
155
-
156
- async generateCssForClasses(classes: string[]): Promise<string> {
157
- const compiler = await getCompiler();
158
- return compiler.build(classes);
159
- }
205
+ readonly version = 4 as const;
206
+
207
+ async resolveTailwindConfig(): Promise<TailwindThemeSubset> {
208
+ const compiler = await getCompiler();
209
+
210
+ // Probe color classes to extract theme variable definitions
211
+ const probeClasses: string[] = [];
212
+ for (const h of HUES) {
213
+ for (const s of SHADES) probeClasses.push(`bg-${h}-${s}`);
214
+ }
215
+ for (const s of SPECIAL_COLORS) probeClasses.push(`bg-${s}`);
216
+
217
+ const css = compiler.build(probeClasses);
218
+
219
+ // --- Colors (extracted from CSS custom properties) ---
220
+ const colorVars = extractVars(css, "color");
221
+ const colors: Record<string, unknown> = {};
222
+ for (const [name, value] of colorVars) {
223
+ const dashIdx = name.lastIndexOf("-");
224
+ if (dashIdx > 0) {
225
+ const hue = name.substring(0, dashIdx);
226
+ const shade = name.substring(dashIdx + 1);
227
+ if (/^\d+$/.test(shade)) {
228
+ if (!colors[hue]) colors[hue] = {};
229
+ (colors[hue] as Record<string, string>)[shade] = value;
230
+ continue;
231
+ }
232
+ }
233
+ colors[name] = value;
234
+ }
235
+ if (!colors["transparent"]) colors["transparent"] = "transparent";
236
+
237
+ // --- Spacing (v4 uses calc(var(--spacing) * N)) ---
238
+ const spacing: Record<string, string> = {};
239
+ for (const k of SPACING_KEYS) {
240
+ spacing[k] =
241
+ k === "px" ? "1px" : k === "0" ? "0px" : `calc(var(--spacing) * ${k})`;
242
+ }
243
+
244
+ // --- Font size, weight, border radius (static scales in v4) ---
245
+ const fontSize: Record<string, unknown> = {};
246
+ for (const k of FONT_SIZE_KEYS) fontSize[k] = k;
247
+
248
+ const fontWeight: Record<string, unknown> = {};
249
+ for (const k of FONT_WEIGHT_KEYS) fontWeight[k] = k;
250
+
251
+ const borderRadius: Record<string, string> = {};
252
+ for (const k of BORDER_RADIUS_KEYS)
253
+ borderRadius[k || "DEFAULT"] = k || "DEFAULT";
254
+
255
+ const result: TailwindThemeSubset = {
256
+ spacing,
257
+ colors,
258
+ fontSize,
259
+ fontWeight,
260
+ borderRadius,
261
+ };
262
+ console.error("[tailwind] v4 resolved theme:", {
263
+ colors: Object.keys(colors).length + " entries",
264
+ spacing: Object.keys(spacing).length + " entries",
265
+ });
266
+ return result;
267
+ }
268
+
269
+ async generateCssForClasses(classes: string[]): Promise<string> {
270
+ const compiler = await getCompiler();
271
+ return compiler.build(classes);
272
+ }
160
273
  }
package/shared/types.ts CHANGED
@@ -114,12 +114,32 @@ export interface PatchPreviewMessage {
114
114
  newClass: string;
115
115
  }
116
116
 
117
+ /** Panel → Overlay: live-preview multiple class swaps atomically */
118
+ export interface PatchPreviewBatchMessage {
119
+ type: 'PATCH_PREVIEW_BATCH';
120
+ to: 'overlay';
121
+ pairs: Array<{ oldClass: string; newClass: string }>;
122
+ }
123
+
117
124
  /** Panel → Overlay: revert any active preview */
118
125
  export interface PatchRevertMessage {
119
126
  type: 'PATCH_REVERT';
120
127
  to: 'overlay';
121
128
  }
122
129
 
130
+ /**
131
+ * Panel → Overlay: undo a previously staged class change.
132
+ * Applies oldClass → newClass to the DOM and commits it as the new baseline,
133
+ * WITHOUT sending anything to the server.
134
+ * Use when the user stages back to the original value, removing a draft patch.
135
+ */
136
+ export interface PatchRevertStagedMessage {
137
+ type: 'PATCH_REVERT_STAGED';
138
+ to: 'overlay';
139
+ oldClass: string; // currently in the DOM (the staged newClass)
140
+ newClass: string; // what to restore to (the original class)
141
+ }
142
+
123
143
  /** Panel → Overlay: stage a change (overlay fills context, sends PATCH_STAGED to server) */
124
144
  export interface PatchStageMessage {
125
145
  type: 'PATCH_STAGE';
@@ -257,6 +277,11 @@ export interface DesignCloseMessage {
257
277
  type: 'DESIGN_CLOSE';
258
278
  }
259
279
 
280
+ /** Panel → Overlay: close the inspector panel */
281
+ export interface ClosePanelMessage {
282
+ type: 'CLOSE_PANEL';
283
+ }
284
+
260
285
  // ---------------------------------------------------------------------------
261
286
  // Union types
262
287
  // ---------------------------------------------------------------------------
@@ -264,12 +289,14 @@ export interface DesignCloseMessage {
264
289
  export type OverlayToPanel = ElementSelectedMessage;
265
290
  export type PanelToOverlay =
266
291
  | PatchPreviewMessage
292
+ | PatchPreviewBatchMessage
267
293
  | PatchRevertMessage
268
294
  | PatchStageMessage
269
295
  | ClearHighlightsMessage
270
296
  | SwitchContainerMessage
271
297
  | InsertDesignCanvasMessage
272
- | CaptureScreenshotMessage;
298
+ | CaptureScreenshotMessage
299
+ | ClosePanelMessage;
273
300
  export type OverlayToServer = PatchStagedMessage;
274
301
  export type PanelToServer = PatchCommitMessage | MessageStageMessage;
275
302
  export type ClientToServer =
@@ -308,5 +335,6 @@ export type AnyMessage =
308
335
  | ElementContextMessage
309
336
  | DesignSubmitMessage
310
337
  | DesignCloseMessage
338
+ | ClosePanelMessage
311
339
  | PingMessage
312
340
  | PongMessage;