@bitovi/vybit 0.4.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.
@@ -0,0 +1,1547 @@
1
+ "use strict";
2
+ (() => {
3
+ // overlay/src/ws.ts
4
+ var socket = null;
5
+ var connected = false;
6
+ var handlers = [];
7
+ function connect(url = "ws://localhost:3333") {
8
+ socket = new WebSocket(url);
9
+ socket.addEventListener("open", () => {
10
+ connected = true;
11
+ send({ type: "REGISTER", role: "overlay" });
12
+ window.dispatchEvent(new CustomEvent("overlay-ws-connected"));
13
+ });
14
+ socket.addEventListener("close", () => {
15
+ connected = false;
16
+ socket = null;
17
+ window.dispatchEvent(new CustomEvent("overlay-ws-disconnected"));
18
+ setTimeout(() => connect(url), 3e3);
19
+ });
20
+ socket.addEventListener("message", (event) => {
21
+ try {
22
+ const data = JSON.parse(event.data);
23
+ for (const handler of handlers) {
24
+ handler(data);
25
+ }
26
+ } catch (err) {
27
+ console.error("[tw-overlay] Failed to parse message:", err);
28
+ }
29
+ });
30
+ socket.addEventListener("error", (err) => {
31
+ console.error("[tw-overlay] WebSocket error:", err);
32
+ });
33
+ }
34
+ function send(data) {
35
+ if (connected && socket) {
36
+ socket.send(JSON.stringify(data));
37
+ } else {
38
+ console.warn("[tw-overlay] Cannot send \u2014 not connected");
39
+ }
40
+ }
41
+ function onMessage(handler) {
42
+ handlers.push(handler);
43
+ }
44
+ function sendTo(role, data) {
45
+ send({ ...data, to: role });
46
+ }
47
+
48
+ // overlay/src/fiber.ts
49
+ function getFiber(domNode) {
50
+ const key = Object.keys(domNode).find((k) => k.startsWith("__reactFiber$"));
51
+ return key ? domNode[key] : null;
52
+ }
53
+ function findComponentBoundary(fiber) {
54
+ let current = fiber.return;
55
+ while (current) {
56
+ if (typeof current.type === "function") {
57
+ return {
58
+ componentType: current.type,
59
+ componentName: current.type.displayName || current.type.name || "Unknown",
60
+ componentFiber: current
61
+ };
62
+ }
63
+ current = current.return;
64
+ }
65
+ return null;
66
+ }
67
+ function getRootFiber() {
68
+ const candidateIds = ["root", "app", "__next"];
69
+ for (const id of candidateIds) {
70
+ const el = document.getElementById(id);
71
+ if (!el) continue;
72
+ const key = Object.keys(el).find((k) => k.startsWith("__reactContainer$"));
73
+ if (key) {
74
+ const container = el[key];
75
+ if (container?.stateNode?.current) {
76
+ return container.stateNode.current;
77
+ }
78
+ return container;
79
+ }
80
+ }
81
+ const reactRoot = document.querySelector("[data-reactroot]");
82
+ if (reactRoot) {
83
+ return getFiber(reactRoot);
84
+ }
85
+ return null;
86
+ }
87
+ function findAllInstances(rootFiber, componentType) {
88
+ const results = [];
89
+ function walk(fiber) {
90
+ if (!fiber) return;
91
+ if (fiber.type === componentType) {
92
+ results.push(fiber);
93
+ }
94
+ walk(fiber.child);
95
+ walk(fiber.sibling);
96
+ }
97
+ walk(rootFiber);
98
+ return results;
99
+ }
100
+ function getChildPath(componentFiber, targetFiber) {
101
+ const path = [];
102
+ let current = targetFiber;
103
+ while (current && current !== componentFiber) {
104
+ const parent = current.return;
105
+ if (!parent) break;
106
+ let index = 0;
107
+ let sibling = parent.child;
108
+ while (sibling && sibling !== current) {
109
+ sibling = sibling.sibling;
110
+ index++;
111
+ }
112
+ path.push(index);
113
+ current = parent;
114
+ }
115
+ path.reverse();
116
+ return path;
117
+ }
118
+ function resolvePathToDOM(instanceFiber, path) {
119
+ let current = instanceFiber;
120
+ for (const index of path) {
121
+ if (!current) return null;
122
+ current = current.child;
123
+ if (!current) return null;
124
+ for (let i = 0; i < index; i++) {
125
+ if (!current) return null;
126
+ current = current.sibling;
127
+ }
128
+ }
129
+ if (!current) return null;
130
+ return getDOMNode(current);
131
+ }
132
+ function getDOMNode(fiber) {
133
+ if (fiber.stateNode instanceof HTMLElement) {
134
+ return fiber.stateNode;
135
+ }
136
+ let child = fiber.child;
137
+ while (child) {
138
+ if (child.tag === 5 && child.stateNode instanceof HTMLElement) {
139
+ return child.stateNode;
140
+ }
141
+ const result = getDOMNode(child);
142
+ if (result) return result;
143
+ child = child.sibling;
144
+ }
145
+ return null;
146
+ }
147
+ function findEquivalentDescendant(container, clicked, containerNode) {
148
+ const chain = [];
149
+ let el = clicked;
150
+ while (el && el !== containerNode) {
151
+ chain.unshift(el);
152
+ el = el.parentElement;
153
+ }
154
+ if (chain.length === 0) return container;
155
+ let node = container;
156
+ for (const ancestor of chain) {
157
+ const match = Array.from(node.children).find(
158
+ (c) => c instanceof HTMLElement && c.tagName === ancestor.tagName && c.className === ancestor.className
159
+ ) ?? null;
160
+ if (!match) return null;
161
+ node = match;
162
+ }
163
+ return node;
164
+ }
165
+ function findInlineRepeatedNodes(targetFiber, boundaryFiber, minSiblings = 3) {
166
+ const clickedNode = getDOMNode(targetFiber);
167
+ if (!clickedNode) return [];
168
+ let current = targetFiber;
169
+ let bestResult = [];
170
+ while (current && current !== boundaryFiber) {
171
+ const parent = current.return;
172
+ if (!parent) break;
173
+ const sameType = [];
174
+ let child = parent.child;
175
+ while (child) {
176
+ if (child.type === current.type) {
177
+ sameType.push(child);
178
+ }
179
+ child = child.sibling;
180
+ }
181
+ if (sameType.length >= minSiblings && sameType.length > bestResult.length) {
182
+ const containerNode = getDOMNode(current);
183
+ if (!containerNode) {
184
+ current = parent;
185
+ continue;
186
+ }
187
+ const containerNodes = [];
188
+ for (const sib of sameType) {
189
+ const node = getDOMNode(sib);
190
+ if (node) containerNodes.push(node);
191
+ }
192
+ const majorityClass = containerNodes.map((n) => n.className).sort(
193
+ (a, b) => containerNodes.filter((n) => n.className === b).length - containerNodes.filter((n) => n.className === a).length
194
+ )[0];
195
+ const outliers = containerNodes.filter((n) => n.className !== majorityClass);
196
+ if (outliers.length > 1) {
197
+ current = parent;
198
+ continue;
199
+ }
200
+ const results = [];
201
+ for (const sibContainer of containerNodes) {
202
+ const equiv = findEquivalentDescendant(sibContainer, clickedNode, containerNode);
203
+ if (equiv) results.push(equiv);
204
+ }
205
+ if (results.length > bestResult.length) {
206
+ bestResult = results;
207
+ }
208
+ }
209
+ current = parent;
210
+ }
211
+ return bestResult;
212
+ }
213
+
214
+ // overlay/src/class-parser.ts
215
+ function deriveValueType(themeKey) {
216
+ if (themeKey === "colors") return "color";
217
+ if (themeKey !== null) return "scalar";
218
+ return "enum";
219
+ }
220
+ var PREFIX_MAP = [
221
+ // Spacing
222
+ { prefix: "px-", category: "spacing", themeKey: "spacing" },
223
+ { prefix: "py-", category: "spacing", themeKey: "spacing" },
224
+ { prefix: "pt-", category: "spacing", themeKey: "spacing" },
225
+ { prefix: "pr-", category: "spacing", themeKey: "spacing" },
226
+ { prefix: "pb-", category: "spacing", themeKey: "spacing" },
227
+ { prefix: "pl-", category: "spacing", themeKey: "spacing" },
228
+ { prefix: "ps-", category: "spacing", themeKey: "spacing" },
229
+ { prefix: "pe-", category: "spacing", themeKey: "spacing" },
230
+ { prefix: "p-", category: "spacing", themeKey: "spacing" },
231
+ { prefix: "mx-", category: "spacing", themeKey: "spacing" },
232
+ { prefix: "my-", category: "spacing", themeKey: "spacing" },
233
+ { prefix: "mt-", category: "spacing", themeKey: "spacing" },
234
+ { prefix: "mr-", category: "spacing", themeKey: "spacing" },
235
+ { prefix: "mb-", category: "spacing", themeKey: "spacing" },
236
+ { prefix: "ml-", category: "spacing", themeKey: "spacing" },
237
+ { prefix: "ms-", category: "spacing", themeKey: "spacing" },
238
+ { prefix: "me-", category: "spacing", themeKey: "spacing" },
239
+ { prefix: "m-", category: "spacing", themeKey: "spacing" },
240
+ { prefix: "gap-x-", category: "spacing", themeKey: "spacing" },
241
+ { prefix: "gap-y-", category: "spacing", themeKey: "spacing" },
242
+ { prefix: "gap-", category: "spacing", themeKey: "spacing" },
243
+ { prefix: "space-x-", category: "spacing", themeKey: "spacing" },
244
+ { prefix: "space-y-", category: "spacing", themeKey: "spacing" },
245
+ // Sizing
246
+ { prefix: "min-w-", category: "sizing", themeKey: "spacing" },
247
+ { prefix: "max-w-", category: "sizing", themeKey: "spacing" },
248
+ { prefix: "min-h-", category: "sizing", themeKey: "spacing" },
249
+ { prefix: "max-h-", category: "sizing", themeKey: "spacing" },
250
+ { prefix: "size-", category: "sizing", themeKey: "spacing" },
251
+ { prefix: "w-", category: "sizing", themeKey: "spacing" },
252
+ { prefix: "h-", category: "sizing", themeKey: "spacing" },
253
+ // Color
254
+ { prefix: "bg-", category: "color", themeKey: "colors" },
255
+ { prefix: "ring-", category: "color", themeKey: "colors" },
256
+ { prefix: "outline-", category: "color", themeKey: "colors" },
257
+ { prefix: "fill-", category: "color", themeKey: "colors" },
258
+ { prefix: "stroke-", category: "color", themeKey: "colors" },
259
+ { prefix: "decoration-", category: "color", themeKey: "colors" },
260
+ // Typography
261
+ { prefix: "font-", category: "typography", themeKey: "fontWeight" },
262
+ { prefix: "leading-", category: "typography", themeKey: "lineHeight" },
263
+ { prefix: "tracking-", category: "typography", themeKey: "letterSpacing" },
264
+ // Borders
265
+ { prefix: "rounded-tl-", category: "borders", themeKey: "borderRadius" },
266
+ { prefix: "rounded-tr-", category: "borders", themeKey: "borderRadius" },
267
+ { prefix: "rounded-br-", category: "borders", themeKey: "borderRadius" },
268
+ { prefix: "rounded-bl-", category: "borders", themeKey: "borderRadius" },
269
+ { prefix: "rounded-t-", category: "borders", themeKey: "borderRadius" },
270
+ { prefix: "rounded-r-", category: "borders", themeKey: "borderRadius" },
271
+ { prefix: "rounded-b-", category: "borders", themeKey: "borderRadius" },
272
+ { prefix: "rounded-l-", category: "borders", themeKey: "borderRadius" },
273
+ { prefix: "rounded-", category: "borders", themeKey: "borderRadius" },
274
+ { prefix: "border-t-", category: "borders", themeKey: "borderWidth" },
275
+ { prefix: "border-r-", category: "borders", themeKey: "borderWidth" },
276
+ { prefix: "border-b-", category: "borders", themeKey: "borderWidth" },
277
+ { prefix: "border-l-", category: "borders", themeKey: "borderWidth" },
278
+ // Effects
279
+ { prefix: "opacity-", category: "effects", themeKey: null },
280
+ { prefix: "shadow-", category: "effects", themeKey: null },
281
+ // Layout
282
+ { prefix: "inset-", category: "layout", themeKey: "spacing" },
283
+ { prefix: "top-", category: "layout", themeKey: "spacing" },
284
+ { prefix: "right-", category: "layout", themeKey: "spacing" },
285
+ { prefix: "bottom-", category: "layout", themeKey: "spacing" },
286
+ { prefix: "left-", category: "layout", themeKey: "spacing" },
287
+ { prefix: "z-", category: "layout", themeKey: null },
288
+ // Flexbox & Grid
289
+ { prefix: "basis-", category: "flexbox", themeKey: "spacing" },
290
+ { prefix: "grid-cols-", category: "flexbox", themeKey: null },
291
+ { prefix: "grid-rows-", category: "flexbox", themeKey: null },
292
+ { prefix: "col-span-", category: "flexbox", themeKey: null },
293
+ { prefix: "row-span-", category: "flexbox", themeKey: null },
294
+ { prefix: "order-", category: "flexbox", themeKey: null }
295
+ ].sort((a, b) => b.prefix.length - a.prefix.length);
296
+ var BORDER_WIDTH_VALUES = /* @__PURE__ */ new Set(["0", "2", "4", "8"]);
297
+ var BORDER_STYLE_KEYWORDS = /* @__PURE__ */ new Set(["solid", "dashed", "dotted", "double", "hidden", "none"]);
298
+ var FONT_SIZE_TOKENS = /* @__PURE__ */ new Set(["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"]);
299
+ var TEXT_ALIGN_KEYWORDS = /* @__PURE__ */ new Set(["left", "center", "right", "justify", "start", "end"]);
300
+ var EXACT_MATCH_MAP = {
301
+ // Borders
302
+ "rounded": { category: "borders", themeKey: "borderRadius" },
303
+ "border": { category: "borders", themeKey: "borderWidth" },
304
+ // Effects
305
+ "shadow": { category: "effects", themeKey: null },
306
+ // Typography
307
+ "underline": { category: "typography", themeKey: null },
308
+ "overline": { category: "typography", themeKey: null },
309
+ "line-through": { category: "typography", themeKey: null },
310
+ "no-underline": { category: "typography", themeKey: null },
311
+ "uppercase": { category: "typography", themeKey: null },
312
+ "lowercase": { category: "typography", themeKey: null },
313
+ "capitalize": { category: "typography", themeKey: null },
314
+ "normal-case": { category: "typography", themeKey: null },
315
+ "truncate": { category: "typography", themeKey: null },
316
+ "italic": { category: "typography", themeKey: null },
317
+ "not-italic": { category: "typography", themeKey: null },
318
+ // Layout
319
+ "block": { category: "layout", themeKey: null },
320
+ "inline-block": { category: "layout", themeKey: null },
321
+ "inline": { category: "layout", themeKey: null },
322
+ "flex": { category: "layout", themeKey: null },
323
+ "inline-flex": { category: "layout", themeKey: null },
324
+ "grid": { category: "layout", themeKey: null },
325
+ "inline-grid": { category: "layout", themeKey: null },
326
+ "hidden": { category: "layout", themeKey: null },
327
+ "table": { category: "layout", themeKey: null },
328
+ "contents": { category: "layout", themeKey: null },
329
+ "static": { category: "layout", themeKey: null },
330
+ "fixed": { category: "layout", themeKey: null },
331
+ "absolute": { category: "layout", themeKey: null },
332
+ "relative": { category: "layout", themeKey: null },
333
+ "sticky": { category: "layout", themeKey: null },
334
+ // Flexbox
335
+ "flex-row": { category: "flexbox", themeKey: null },
336
+ "flex-row-reverse": { category: "flexbox", themeKey: null },
337
+ "flex-col": { category: "flexbox", themeKey: null },
338
+ "flex-col-reverse": { category: "flexbox", themeKey: null },
339
+ "flex-wrap": { category: "flexbox", themeKey: null },
340
+ "flex-wrap-reverse": { category: "flexbox", themeKey: null },
341
+ "flex-nowrap": { category: "flexbox", themeKey: null },
342
+ "flex-1": { category: "flexbox", themeKey: null },
343
+ "flex-auto": { category: "flexbox", themeKey: null },
344
+ "flex-initial": { category: "flexbox", themeKey: null },
345
+ "flex-none": { category: "flexbox", themeKey: null },
346
+ "grow": { category: "flexbox", themeKey: null },
347
+ "grow-0": { category: "flexbox", themeKey: null },
348
+ "shrink": { category: "flexbox", themeKey: null },
349
+ "shrink-0": { category: "flexbox", themeKey: null },
350
+ "justify-start": { category: "flexbox", themeKey: null },
351
+ "justify-end": { category: "flexbox", themeKey: null },
352
+ "justify-center": { category: "flexbox", themeKey: null },
353
+ "justify-between": { category: "flexbox", themeKey: null },
354
+ "justify-around": { category: "flexbox", themeKey: null },
355
+ "justify-evenly": { category: "flexbox", themeKey: null },
356
+ "justify-stretch": { category: "flexbox", themeKey: null },
357
+ "items-start": { category: "flexbox", themeKey: null },
358
+ "items-end": { category: "flexbox", themeKey: null },
359
+ "items-center": { category: "flexbox", themeKey: null },
360
+ "items-baseline": { category: "flexbox", themeKey: null },
361
+ "items-stretch": { category: "flexbox", themeKey: null },
362
+ "self-auto": { category: "flexbox", themeKey: null },
363
+ "self-start": { category: "flexbox", themeKey: null },
364
+ "self-end": { category: "flexbox", themeKey: null },
365
+ "self-center": { category: "flexbox", themeKey: null },
366
+ "self-stretch": { category: "flexbox", themeKey: null },
367
+ "self-baseline": { category: "flexbox", themeKey: null }
368
+ };
369
+ 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:)+/;
370
+ function hasVariantPrefix(cls) {
371
+ return VARIANT_PREFIXES.test(cls);
372
+ }
373
+ function parseTextClass(value) {
374
+ if (FONT_SIZE_TOKENS.has(value)) {
375
+ return { category: "typography", valueType: "scalar", prefix: "text-", value, fullClass: `text-${value}`, themeKey: "fontSize" };
376
+ }
377
+ if (TEXT_ALIGN_KEYWORDS.has(value)) {
378
+ return { category: "typography", valueType: "enum", prefix: "text-", value, fullClass: `text-${value}`, themeKey: null };
379
+ }
380
+ return { category: "color", valueType: "color", prefix: "text-", value, fullClass: `text-${value}`, themeKey: "colors" };
381
+ }
382
+ function parseBorderClass(value) {
383
+ if (BORDER_WIDTH_VALUES.has(value)) {
384
+ return { category: "borders", valueType: "scalar", prefix: "border-", value, fullClass: `border-${value}`, themeKey: "borderWidth" };
385
+ }
386
+ if (BORDER_STYLE_KEYWORDS.has(value)) {
387
+ return { category: "borders", valueType: "enum", prefix: "border-", value, fullClass: `border-${value}`, themeKey: null };
388
+ }
389
+ return { category: "color", valueType: "color", prefix: "border-", value, fullClass: `border-${value}`, themeKey: "colors" };
390
+ }
391
+ function parseClasses(classString) {
392
+ const results = [];
393
+ const classes = classString.trim().split(/\s+/).filter(Boolean);
394
+ for (const cls of classes) {
395
+ if (hasVariantPrefix(cls)) continue;
396
+ const exact = EXACT_MATCH_MAP[cls];
397
+ if (exact) {
398
+ results.push({
399
+ category: exact.category,
400
+ valueType: deriveValueType(exact.themeKey),
401
+ prefix: cls,
402
+ value: "",
403
+ fullClass: cls,
404
+ themeKey: exact.themeKey
405
+ });
406
+ continue;
407
+ }
408
+ if (cls.startsWith("text-")) {
409
+ const value = cls.slice(5);
410
+ const parsed = parseTextClass(value);
411
+ if (parsed) results.push(parsed);
412
+ continue;
413
+ }
414
+ if (cls.startsWith("border-")) {
415
+ const value = cls.slice(7);
416
+ let longerMatch = false;
417
+ for (const entry of PREFIX_MAP) {
418
+ if (entry.prefix !== "border-" && cls.startsWith(entry.prefix)) {
419
+ longerMatch = true;
420
+ results.push({
421
+ category: entry.category,
422
+ valueType: deriveValueType(entry.themeKey),
423
+ prefix: entry.prefix,
424
+ value: cls.slice(entry.prefix.length),
425
+ fullClass: cls,
426
+ themeKey: entry.themeKey
427
+ });
428
+ break;
429
+ }
430
+ }
431
+ if (!longerMatch) {
432
+ const parsed = parseBorderClass(value);
433
+ if (parsed) results.push(parsed);
434
+ }
435
+ continue;
436
+ }
437
+ let matched = false;
438
+ for (const entry of PREFIX_MAP) {
439
+ if (cls.startsWith(entry.prefix)) {
440
+ results.push({
441
+ category: entry.category,
442
+ valueType: deriveValueType(entry.themeKey),
443
+ prefix: entry.prefix,
444
+ value: cls.slice(entry.prefix.length),
445
+ fullClass: cls,
446
+ themeKey: entry.themeKey
447
+ });
448
+ matched = true;
449
+ break;
450
+ }
451
+ }
452
+ if (!matched) {
453
+ }
454
+ }
455
+ return results;
456
+ }
457
+
458
+ // overlay/src/context.ts
459
+ function buildContext(target, oldClass, newClass, originalClassMap) {
460
+ const ancestors = [];
461
+ let current = target;
462
+ while (current && current !== document.documentElement) {
463
+ ancestors.push(current);
464
+ current = current.parentElement;
465
+ }
466
+ ancestors.reverse();
467
+ return buildLevel(ancestors, 0, target, oldClass, newClass, originalClassMap, 0);
468
+ }
469
+ function buildLevel(ancestors, ancestorIndex, target, oldClass, newClass, originalClassMap, indent) {
470
+ const el = ancestors[ancestorIndex];
471
+ const pad = " ".repeat(indent);
472
+ const tag = el.tagName.toLowerCase();
473
+ let attrs = "";
474
+ if (el.id) attrs += ` id="${el.id}"`;
475
+ const originalClass = originalClassMap.get(el);
476
+ const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
477
+ if (classStr) {
478
+ attrs += ` class="${classStr}"`;
479
+ }
480
+ const isTarget = el === target;
481
+ if (isTarget) {
482
+ const text = getInnerText(el);
483
+ const textNode = text ? `
484
+ ${pad} ${text}` : "";
485
+ return `${pad}<${tag}${attrs}> <!-- TARGET: change ${oldClass} \u2192 ${newClass} -->${textNode}
486
+ ${pad}</${tag}>`;
487
+ }
488
+ if (ancestorIndex >= ancestors.length - 1) {
489
+ return `${pad}<${tag}${attrs} />`;
490
+ }
491
+ const nextAncestor = ancestors[ancestorIndex + 1];
492
+ const children = Array.from(el.children);
493
+ const relevantIndex = children.indexOf(nextAncestor);
494
+ let inner = "";
495
+ if (relevantIndex === -1) {
496
+ inner = buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1);
497
+ } else {
498
+ const start = Math.max(0, relevantIndex - 3);
499
+ const end = Math.min(children.length - 1, relevantIndex + 3);
500
+ if (start > 0) {
501
+ inner += `${pad} ...
502
+ `;
503
+ }
504
+ for (let i = start; i <= end; i++) {
505
+ if (i === relevantIndex) {
506
+ inner += buildLevel(ancestors, ancestorIndex + 1, target, oldClass, newClass, originalClassMap, indent + 1) + "\n";
507
+ } else {
508
+ inner += renderSiblingNode(children[i], indent + 1, originalClassMap) + "\n";
509
+ }
510
+ }
511
+ if (end < children.length - 1) {
512
+ inner += `${pad} ...
513
+ `;
514
+ }
515
+ }
516
+ return `${pad}<${tag}${attrs}>
517
+ ${inner}${pad}</${tag}>`;
518
+ }
519
+ function renderSiblingNode(el, indent, originalClassMap) {
520
+ const pad = " ".repeat(indent);
521
+ const tag = el.tagName.toLowerCase();
522
+ let attrs = "";
523
+ if (el.id) attrs += ` id="${el.id}"`;
524
+ const originalClass = originalClassMap.get(el);
525
+ const classStr = originalClass != null ? originalClass.trim() : typeof el.className === "string" ? el.className.trim() : "";
526
+ if (classStr) {
527
+ attrs += ` class="${classStr}"`;
528
+ }
529
+ const text = getInnerText(el);
530
+ if (!el.id && (!el.className || !el.className.trim()) && !text) {
531
+ return `${pad}<${tag}>...</${tag}>`;
532
+ }
533
+ if (text) {
534
+ return `${pad}<${tag}${attrs}>
535
+ ${pad} ${text}
536
+ ${pad}</${tag}>`;
537
+ }
538
+ if (el.children.length > 0) {
539
+ return `${pad}<${tag}${attrs}>
540
+ ${pad} ...
541
+ ${pad}</${tag}>`;
542
+ }
543
+ return `${pad}<${tag}${attrs} />`;
544
+ }
545
+ function getInnerText(el) {
546
+ let text = "";
547
+ for (const node of Array.from(el.childNodes)) {
548
+ if (node.nodeType === Node.TEXT_NODE) {
549
+ text += node.textContent || "";
550
+ }
551
+ }
552
+ text = text.trim();
553
+ if (text.length > 60) text = text.slice(0, 57) + "...";
554
+ return text;
555
+ }
556
+
557
+ // overlay/src/patcher.ts
558
+ var previewState = null;
559
+ var previewStyleEl = null;
560
+ var previewGeneration = 0;
561
+ async function applyPreview(elements, oldClass, newClass, serverOrigin) {
562
+ const gen = ++previewGeneration;
563
+ if (!previewState) {
564
+ previewState = {
565
+ elements,
566
+ originalClasses: elements.map((n) => n.className)
567
+ };
568
+ }
569
+ try {
570
+ const res = await fetch(`${serverOrigin}/css`, {
571
+ method: "POST",
572
+ headers: { "Content-Type": "application/json" },
573
+ body: JSON.stringify({ classes: [newClass] })
574
+ });
575
+ if (gen !== previewGeneration) return;
576
+ const { css } = await res.json();
577
+ if (gen !== previewGeneration) return;
578
+ if (!previewStyleEl) {
579
+ previewStyleEl = document.createElement("style");
580
+ previewStyleEl.setAttribute("data-tw-preview", "");
581
+ document.head.appendChild(previewStyleEl);
582
+ }
583
+ previewStyleEl.textContent = css;
584
+ } catch {
585
+ }
586
+ if (gen !== previewGeneration) return;
587
+ if (previewState) {
588
+ for (let i = 0; i < previewState.elements.length; i++) {
589
+ previewState.elements[i].className = previewState.originalClasses[i];
590
+ }
591
+ }
592
+ for (const node of elements) {
593
+ if (oldClass) node.classList.remove(oldClass);
594
+ node.classList.add(newClass);
595
+ }
596
+ }
597
+ function revertPreview() {
598
+ previewGeneration++;
599
+ if (previewState) {
600
+ for (let i = 0; i < previewState.elements.length; i++) {
601
+ previewState.elements[i].className = previewState.originalClasses[i];
602
+ }
603
+ previewState = null;
604
+ }
605
+ previewStyleEl?.remove();
606
+ previewStyleEl = null;
607
+ }
608
+ function getPreviewState() {
609
+ return previewState;
610
+ }
611
+
612
+ // overlay/src/containers/PopoverContainer.ts
613
+ var PopoverContainer = class {
614
+ constructor(shadowRoot2) {
615
+ this.shadowRoot = shadowRoot2;
616
+ }
617
+ name = "popover";
618
+ host = null;
619
+ open(panelUrl) {
620
+ if (this.host) return;
621
+ const host = document.createElement("div");
622
+ host.className = "container-popover";
623
+ host.style.cssText = `
624
+ position: fixed;
625
+ top: 0;
626
+ right: 0;
627
+ width: 400px;
628
+ height: 100vh;
629
+ z-index: 999999;
630
+ background: #1e1e2e;
631
+ box-shadow: -4px 0 24px rgba(0,0,0,0.3);
632
+ `;
633
+ const iframe = document.createElement("iframe");
634
+ iframe.src = panelUrl;
635
+ iframe.style.cssText = "width:100%; height:100%; border:none;";
636
+ host.appendChild(iframe);
637
+ this.shadowRoot.appendChild(host);
638
+ this.host = host;
639
+ }
640
+ close() {
641
+ if (this.host) {
642
+ this.host.remove();
643
+ this.host = null;
644
+ }
645
+ }
646
+ isOpen() {
647
+ return this.host !== null;
648
+ }
649
+ };
650
+
651
+ // overlay/src/containers/ModalContainer.ts
652
+ var STORAGE_KEY = "tw-modal-bounds";
653
+ function loadBounds() {
654
+ try {
655
+ const stored = localStorage.getItem(STORAGE_KEY);
656
+ if (stored) return JSON.parse(stored);
657
+ } catch {
658
+ }
659
+ return { top: 80, left: Math.max(0, window.innerWidth - 440), width: 400, height: 600 };
660
+ }
661
+ function saveBounds(bounds) {
662
+ try {
663
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(bounds));
664
+ } catch {
665
+ }
666
+ }
667
+ var ModalContainer = class {
668
+ constructor(shadowRoot2) {
669
+ this.shadowRoot = shadowRoot2;
670
+ }
671
+ name = "modal";
672
+ host = null;
673
+ bounds = loadBounds();
674
+ open(panelUrl) {
675
+ if (this.host) return;
676
+ this.bounds = loadBounds();
677
+ const host = document.createElement("div");
678
+ host.className = "container-modal";
679
+ this.applyBounds(host);
680
+ host.style.position = "fixed";
681
+ host.style.zIndex = "999999";
682
+ host.style.background = "#1e1e2e";
683
+ host.style.borderRadius = "8px";
684
+ host.style.boxShadow = "0 8px 32px rgba(0,0,0,0.4)";
685
+ host.style.display = "flex";
686
+ host.style.flexDirection = "column";
687
+ host.style.overflow = "hidden";
688
+ const handle = document.createElement("div");
689
+ handle.style.cssText = `
690
+ height: 28px;
691
+ background: #181825;
692
+ cursor: move;
693
+ display: flex;
694
+ align-items: center;
695
+ justify-content: center;
696
+ flex-shrink: 0;
697
+ user-select: none;
698
+ `;
699
+ handle.innerHTML = '<span style="color:#585b70;font-size:11px;letter-spacing:2px;">\u22EF\u22EF\u22EF</span>';
700
+ this.setupDrag(handle, host);
701
+ host.appendChild(handle);
702
+ const iframe = document.createElement("iframe");
703
+ iframe.src = panelUrl;
704
+ iframe.style.cssText = "flex:1; border:none; width:100%;";
705
+ host.appendChild(iframe);
706
+ const gripper = document.createElement("div");
707
+ gripper.style.cssText = `
708
+ position: absolute;
709
+ bottom: 0;
710
+ right: 0;
711
+ width: 16px;
712
+ height: 16px;
713
+ cursor: nwse-resize;
714
+ `;
715
+ gripper.innerHTML = '<span style="position:absolute;bottom:2px;right:4px;color:#585b70;font-size:10px;">\u25E2</span>';
716
+ this.setupResize(gripper, host, iframe);
717
+ host.appendChild(gripper);
718
+ this.shadowRoot.appendChild(host);
719
+ this.host = host;
720
+ }
721
+ close() {
722
+ if (this.host) {
723
+ this.host.remove();
724
+ this.host = null;
725
+ }
726
+ }
727
+ isOpen() {
728
+ return this.host !== null;
729
+ }
730
+ applyBounds(el) {
731
+ el.style.top = `${this.bounds.top}px`;
732
+ el.style.left = `${this.bounds.left}px`;
733
+ el.style.width = `${this.bounds.width}px`;
734
+ el.style.height = `${this.bounds.height}px`;
735
+ }
736
+ setupDrag(handle, host) {
737
+ let startX = 0, startY = 0, startLeft = 0, startTop = 0;
738
+ const onMove = (e) => {
739
+ this.bounds.left = startLeft + (e.clientX - startX);
740
+ this.bounds.top = startTop + (e.clientY - startY);
741
+ host.style.left = `${this.bounds.left}px`;
742
+ host.style.top = `${this.bounds.top}px`;
743
+ };
744
+ const onUp = () => {
745
+ document.removeEventListener("mousemove", onMove);
746
+ document.removeEventListener("mouseup", onUp);
747
+ saveBounds(this.bounds);
748
+ };
749
+ handle.addEventListener("mousedown", (e) => {
750
+ e.preventDefault();
751
+ startX = e.clientX;
752
+ startY = e.clientY;
753
+ startLeft = this.bounds.left;
754
+ startTop = this.bounds.top;
755
+ document.addEventListener("mousemove", onMove);
756
+ document.addEventListener("mouseup", onUp);
757
+ });
758
+ }
759
+ setupResize(gripper, host, iframe) {
760
+ let startX = 0, startY = 0, startW = 0, startH = 0;
761
+ const onMove = (e) => {
762
+ this.bounds.width = Math.max(300, startW + (e.clientX - startX));
763
+ this.bounds.height = Math.max(200, startH + (e.clientY - startY));
764
+ host.style.width = `${this.bounds.width}px`;
765
+ host.style.height = `${this.bounds.height}px`;
766
+ };
767
+ const onUp = () => {
768
+ iframe.style.pointerEvents = "";
769
+ document.removeEventListener("mousemove", onMove);
770
+ document.removeEventListener("mouseup", onUp);
771
+ saveBounds(this.bounds);
772
+ };
773
+ gripper.addEventListener("mousedown", (e) => {
774
+ e.preventDefault();
775
+ iframe.style.pointerEvents = "none";
776
+ startX = e.clientX;
777
+ startY = e.clientY;
778
+ startW = this.bounds.width;
779
+ startH = this.bounds.height;
780
+ document.addEventListener("mousemove", onMove);
781
+ document.addEventListener("mouseup", onUp);
782
+ });
783
+ }
784
+ };
785
+
786
+ // overlay/src/containers/SidebarContainer.ts
787
+ var SidebarContainer = class {
788
+ constructor(shadowRoot2) {
789
+ this.shadowRoot = shadowRoot2;
790
+ }
791
+ name = "sidebar";
792
+ host = null;
793
+ originalPadding = "";
794
+ width = 380;
795
+ open(panelUrl) {
796
+ if (this.host) return;
797
+ this.originalPadding = document.documentElement.style.paddingRight;
798
+ document.documentElement.style.paddingRight = `${this.width}px`;
799
+ const host = document.createElement("div");
800
+ host.className = "container-sidebar";
801
+ host.style.cssText = `
802
+ position: fixed;
803
+ top: 0;
804
+ right: 0;
805
+ width: ${this.width}px;
806
+ height: 100vh;
807
+ z-index: 999999;
808
+ background: #1e1e2e;
809
+ box-shadow: -4px 0 24px rgba(0,0,0,0.3);
810
+ display: flex;
811
+ `;
812
+ const resizeHandle = document.createElement("div");
813
+ resizeHandle.style.cssText = `
814
+ width: 6px;
815
+ cursor: ew-resize;
816
+ background: transparent;
817
+ flex-shrink: 0;
818
+ `;
819
+ resizeHandle.addEventListener("mouseenter", () => {
820
+ resizeHandle.style.background = "#45475a";
821
+ });
822
+ resizeHandle.addEventListener("mouseleave", () => {
823
+ resizeHandle.style.background = "transparent";
824
+ });
825
+ this.setupResize(resizeHandle, host);
826
+ host.appendChild(resizeHandle);
827
+ const iframe = document.createElement("iframe");
828
+ iframe.src = panelUrl;
829
+ iframe.style.cssText = "flex:1; border:none; height:100%;";
830
+ host.appendChild(iframe);
831
+ this.shadowRoot.appendChild(host);
832
+ this.host = host;
833
+ }
834
+ close() {
835
+ if (this.host) {
836
+ this.host.remove();
837
+ this.host = null;
838
+ document.documentElement.style.paddingRight = this.originalPadding;
839
+ }
840
+ }
841
+ isOpen() {
842
+ return this.host !== null;
843
+ }
844
+ setupResize(handle, host) {
845
+ let startX = 0, startW = 0;
846
+ const onMove = (e) => {
847
+ this.width = Math.max(280, startW - (e.clientX - startX));
848
+ host.style.width = `${this.width}px`;
849
+ document.documentElement.style.paddingRight = `${this.width}px`;
850
+ };
851
+ const onUp = () => {
852
+ document.removeEventListener("mousemove", onMove);
853
+ document.removeEventListener("mouseup", onUp);
854
+ };
855
+ handle.addEventListener("mousedown", (e) => {
856
+ e.preventDefault();
857
+ startX = e.clientX;
858
+ startW = this.width;
859
+ document.addEventListener("mousemove", onMove);
860
+ document.addEventListener("mouseup", onUp);
861
+ });
862
+ }
863
+ };
864
+
865
+ // overlay/src/containers/PopupContainer.ts
866
+ var PopupContainer = class {
867
+ name = "popup";
868
+ popup = null;
869
+ open(panelUrl) {
870
+ if (this.popup && !this.popup.closed) {
871
+ this.popup.focus();
872
+ return;
873
+ }
874
+ this.popup = window.open(panelUrl, "tw-panel", "popup,width=420,height=700");
875
+ }
876
+ close() {
877
+ if (this.popup && !this.popup.closed) {
878
+ this.popup.close();
879
+ }
880
+ this.popup = null;
881
+ }
882
+ isOpen() {
883
+ return this.popup !== null && !this.popup.closed;
884
+ }
885
+ };
886
+
887
+ // overlay/src/index.ts
888
+ var shadowRoot;
889
+ var shadowHost;
890
+ var active = false;
891
+ var wasConnected = false;
892
+ var tailwindConfigCache = null;
893
+ var currentEquivalentNodes = [];
894
+ var currentTargetEl = null;
895
+ var currentBoundary = null;
896
+ var containers;
897
+ var activeContainer;
898
+ var OVERLAY_CSS = `
899
+ .toggle-btn {
900
+ position: fixed;
901
+ bottom: 16px;
902
+ right: 16px;
903
+ width: 36px;
904
+ height: 36px;
905
+ border-radius: 50%;
906
+ border: 1px solid #DFE2E2;
907
+ cursor: pointer;
908
+ z-index: 999999;
909
+ background: #F4F5F5;
910
+ color: #687879;
911
+ font-size: 18px;
912
+ line-height: 1;
913
+ display: flex;
914
+ align-items: center;
915
+ justify-content: center;
916
+ box-shadow: 0 2px 8px rgba(0,0,0,0.10);
917
+ transition: background 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s;
918
+ }
919
+ .toggle-btn:hover {
920
+ background: #E6E9E9;
921
+ border-color: #00848B;
922
+ color: #00848B;
923
+ }
924
+ .toggle-btn.active {
925
+ background: #F5532D;
926
+ border-color: #F5532D;
927
+ color: #fff;
928
+ box-shadow: 0 0 0 4px rgba(245, 83, 45, 0.09), 0 2px 8px rgba(0,0,0,0.10);
929
+ }
930
+ .toggle-btn.active:hover {
931
+ background: #C73D26;
932
+ border-color: #C73D26;
933
+ }
934
+ .toast {
935
+ position: fixed;
936
+ top: 16px;
937
+ left: 50%;
938
+ transform: translateX(-50%);
939
+ background: #00464A;
940
+ color: #F4F5F5;
941
+ padding: 8px 16px;
942
+ border-radius: 8px;
943
+ font-size: 12px;
944
+ font-family: 'Inter', system-ui, sans-serif;
945
+ z-index: 999999;
946
+ box-shadow: 0 4px 16px rgba(0,0,0,0.15);
947
+ opacity: 0;
948
+ transition: opacity 0.2s;
949
+ }
950
+ .toast.visible {
951
+ opacity: 1;
952
+ }
953
+ .highlight-overlay {
954
+ position: fixed;
955
+ pointer-events: none;
956
+ border: 2px solid rgba(0, 132, 139, 0.5);
957
+ background: rgba(0, 132, 139, 0.08);
958
+ z-index: 999998;
959
+ transition: all 0.15s ease;
960
+ }
961
+ .draw-btn {
962
+ position: fixed;
963
+ z-index: 999999;
964
+ width: 28px;
965
+ height: 28px;
966
+ border-radius: 6px;
967
+ border: 1px solid #DFE2E2;
968
+ background: #F4F5F5;
969
+ color: #00848B;
970
+ font-size: 14px;
971
+ line-height: 1;
972
+ display: flex;
973
+ align-items: center;
974
+ justify-content: center;
975
+ cursor: pointer;
976
+ box-shadow: 0 2px 8px rgba(0,0,0,0.12);
977
+ transition: background 0.15s, border-color 0.15s, transform 0.1s;
978
+ pointer-events: auto;
979
+ }
980
+ .draw-btn:hover {
981
+ background: #E6E9E9;
982
+ border-color: #00848B;
983
+ transform: scale(1.08);
984
+ }
985
+ .draw-popover {
986
+ position: fixed;
987
+ z-index: 999999;
988
+ background: #fff;
989
+ border: 1px solid #DFE2E2;
990
+ border-radius: 10px;
991
+ box-shadow: 0 8px 32px rgba(0,0,0,0.15);
992
+ padding: 6px 0;
993
+ min-width: 210px;
994
+ font-family: 'Inter', system-ui, sans-serif;
995
+ pointer-events: auto;
996
+ }
997
+ .draw-popover-header {
998
+ padding: 6px 14px 4px;
999
+ font-size: 9px;
1000
+ font-weight: 600;
1001
+ text-transform: uppercase;
1002
+ letter-spacing: 0.08em;
1003
+ color: #687879;
1004
+ }
1005
+ .draw-popover-item {
1006
+ display: flex;
1007
+ align-items: center;
1008
+ gap: 8px;
1009
+ padding: 7px 14px;
1010
+ font-size: 13px;
1011
+ color: #1a2b2c;
1012
+ cursor: pointer;
1013
+ transition: background 0.1s;
1014
+ border: none;
1015
+ background: none;
1016
+ width: 100%;
1017
+ text-align: left;
1018
+ font-family: inherit;
1019
+ }
1020
+ .draw-popover-item:hover {
1021
+ background: rgba(0, 132, 139, 0.06);
1022
+ }
1023
+ .draw-popover-item:hover .draw-popover-icon {
1024
+ color: #00848B;
1025
+ background: rgba(0, 132, 139, 0.08);
1026
+ border-color: #00848B;
1027
+ }
1028
+ .draw-popover-icon {
1029
+ width: 26px;
1030
+ height: 26px;
1031
+ border-radius: 6px;
1032
+ border: 1px solid #DFE2E2;
1033
+ background: #F4F5F5;
1034
+ color: #687879;
1035
+ display: flex;
1036
+ align-items: center;
1037
+ justify-content: center;
1038
+ font-size: 14px;
1039
+ transition: all 0.1s;
1040
+ flex-shrink: 0;
1041
+ }
1042
+ .draw-popover-label {
1043
+ flex: 1;
1044
+ font-weight: 500;
1045
+ }
1046
+ .draw-popover-hint {
1047
+ font-size: 10px;
1048
+ color: #9DAAAB;
1049
+ font-weight: 400;
1050
+ }
1051
+ `;
1052
+ function highlightElement(el) {
1053
+ const rect = el.getBoundingClientRect();
1054
+ const overlay = document.createElement("div");
1055
+ overlay.className = "highlight-overlay";
1056
+ overlay.style.top = `${rect.top}px`;
1057
+ overlay.style.left = `${rect.left}px`;
1058
+ overlay.style.width = `${rect.width}px`;
1059
+ overlay.style.height = `${rect.height}px`;
1060
+ shadowRoot.appendChild(overlay);
1061
+ }
1062
+ function clearHighlights() {
1063
+ shadowRoot.querySelectorAll(".highlight-overlay").forEach((el) => el.remove());
1064
+ removeDrawButton();
1065
+ }
1066
+ var drawBtnEl = null;
1067
+ var drawPopoverEl = null;
1068
+ function removeDrawButton() {
1069
+ drawBtnEl?.remove();
1070
+ drawBtnEl = null;
1071
+ drawPopoverEl?.remove();
1072
+ drawPopoverEl = null;
1073
+ }
1074
+ function showDrawButton(targetEl) {
1075
+ removeDrawButton();
1076
+ const rect = targetEl.getBoundingClientRect();
1077
+ const btn = document.createElement("button");
1078
+ btn.className = "draw-btn";
1079
+ btn.innerHTML = "\u270F";
1080
+ btn.title = "Insert drawing canvas";
1081
+ btn.style.left = `${Math.max(0, rect.left - 34)}px`;
1082
+ btn.style.top = `${rect.top - 2}px`;
1083
+ drawBtnEl = btn;
1084
+ shadowRoot.appendChild(btn);
1085
+ btn.addEventListener("click", (e) => {
1086
+ e.stopPropagation();
1087
+ if (drawPopoverEl) {
1088
+ drawPopoverEl.remove();
1089
+ drawPopoverEl = null;
1090
+ } else {
1091
+ showDrawPopover(btn);
1092
+ }
1093
+ });
1094
+ }
1095
+ function showDrawPopover(anchorBtn) {
1096
+ drawPopoverEl?.remove();
1097
+ const btnRect = anchorBtn.getBoundingClientRect();
1098
+ const popover = document.createElement("div");
1099
+ popover.className = "draw-popover";
1100
+ popover.style.left = `${btnRect.right + 6}px`;
1101
+ popover.style.top = `${btnRect.top}px`;
1102
+ const header = document.createElement("div");
1103
+ header.className = "draw-popover-header";
1104
+ header.textContent = "Insert Drawing Canvas";
1105
+ popover.appendChild(header);
1106
+ const items = [
1107
+ { mode: "before", icon: "\u2191", label: "Before element", hint: "sibling" },
1108
+ { mode: "after", icon: "\u2193", label: "After element", hint: "sibling" },
1109
+ { mode: "first-child", icon: "\u2912", label: "First child", hint: "child" },
1110
+ { mode: "last-child", icon: "\u2913", label: "Last child", hint: "child" }
1111
+ ];
1112
+ for (const item of items) {
1113
+ const row = document.createElement("button");
1114
+ row.className = "draw-popover-item";
1115
+ row.innerHTML = `
1116
+ <span class="draw-popover-icon">${item.icon}</span>
1117
+ <span class="draw-popover-label">${item.label}</span>
1118
+ <span class="draw-popover-hint">${item.hint}</span>
1119
+ `;
1120
+ row.addEventListener("click", (e) => {
1121
+ e.stopPropagation();
1122
+ drawPopoverEl?.remove();
1123
+ drawPopoverEl = null;
1124
+ injectDesignCanvas(item.mode);
1125
+ });
1126
+ popover.appendChild(row);
1127
+ }
1128
+ drawPopoverEl = popover;
1129
+ shadowRoot.appendChild(popover);
1130
+ const closeHandler = (e) => {
1131
+ const path = e.composedPath();
1132
+ if (!path.includes(popover) && !path.includes(anchorBtn)) {
1133
+ drawPopoverEl?.remove();
1134
+ drawPopoverEl = null;
1135
+ document.removeEventListener("click", closeHandler, { capture: true });
1136
+ }
1137
+ };
1138
+ setTimeout(() => {
1139
+ document.addEventListener("click", closeHandler, { capture: true });
1140
+ }, 0);
1141
+ }
1142
+ function getServerOrigin() {
1143
+ const scripts = document.querySelectorAll('script[src*="overlay.js"]');
1144
+ for (const s of scripts) {
1145
+ const src = s.src;
1146
+ if (src) {
1147
+ try {
1148
+ const url = new URL(src);
1149
+ return url.origin;
1150
+ } catch {
1151
+ }
1152
+ }
1153
+ }
1154
+ return "http://localhost:3333";
1155
+ }
1156
+ var SERVER_ORIGIN = getServerOrigin();
1157
+ async function fetchTailwindConfig() {
1158
+ if (tailwindConfigCache) return tailwindConfigCache;
1159
+ try {
1160
+ const res = await fetch(`${SERVER_ORIGIN}/tailwind-config`);
1161
+ tailwindConfigCache = await res.json();
1162
+ return tailwindConfigCache;
1163
+ } catch (err) {
1164
+ console.error("[tw-overlay] Failed to fetch tailwind config:", err);
1165
+ return {};
1166
+ }
1167
+ }
1168
+ async function clickHandler(e) {
1169
+ const composed = e.composedPath();
1170
+ if (composed.some((el) => el === shadowHost)) return;
1171
+ if (composed.some((el) => el instanceof HTMLElement && el.hasAttribute("data-tw-design-canvas"))) return;
1172
+ e.preventDefault();
1173
+ e.stopPropagation();
1174
+ const target = e.target;
1175
+ const fiber = getFiber(target);
1176
+ if (!fiber) {
1177
+ showToast("Could not detect a React component for this element.");
1178
+ return;
1179
+ }
1180
+ const boundary = findComponentBoundary(fiber);
1181
+ if (!boundary) {
1182
+ showToast("Could not detect a React component for this element.");
1183
+ return;
1184
+ }
1185
+ const rootFiber = getRootFiber();
1186
+ if (!rootFiber) {
1187
+ showToast("Could not find React root.");
1188
+ return;
1189
+ }
1190
+ const instances = findAllInstances(rootFiber, boundary.componentType);
1191
+ const path = getChildPath(boundary.componentFiber, fiber);
1192
+ clearHighlights();
1193
+ const equivalentNodes = [];
1194
+ for (const inst of instances) {
1195
+ const node = resolvePathToDOM(inst, path);
1196
+ if (node) {
1197
+ equivalentNodes.push(node);
1198
+ highlightElement(node);
1199
+ }
1200
+ }
1201
+ if (equivalentNodes.length <= 1) {
1202
+ const repeated = findInlineRepeatedNodes(fiber, boundary.componentFiber);
1203
+ if (repeated.length > 0) {
1204
+ clearHighlights();
1205
+ equivalentNodes.length = 0;
1206
+ for (const node of repeated) {
1207
+ equivalentNodes.push(node);
1208
+ highlightElement(node);
1209
+ }
1210
+ }
1211
+ }
1212
+ console.log(`[overlay] ${boundary.componentName} \u2014 ${instances.length} instances, ${equivalentNodes.length} highlighted`);
1213
+ const config = await fetchTailwindConfig();
1214
+ const targetEl = target;
1215
+ const classString = targetEl.className;
1216
+ if (typeof classString !== "string") return;
1217
+ const parsedClasses = parseClasses(classString);
1218
+ if (parsedClasses.length === 0) return;
1219
+ currentEquivalentNodes = equivalentNodes;
1220
+ currentTargetEl = targetEl;
1221
+ currentBoundary = { componentName: boundary.componentName };
1222
+ showDrawButton(targetEl);
1223
+ const panelUrl = `${SERVER_ORIGIN}/panel`;
1224
+ if (!activeContainer.isOpen()) {
1225
+ activeContainer.open(panelUrl);
1226
+ }
1227
+ sendTo("panel", {
1228
+ type: "ELEMENT_SELECTED",
1229
+ componentName: boundary.componentName,
1230
+ instanceCount: equivalentNodes.length,
1231
+ classes: classString,
1232
+ tailwindConfig: config
1233
+ });
1234
+ }
1235
+ function toggleInspect(btn) {
1236
+ active = !active;
1237
+ if (active) {
1238
+ btn.classList.add("active");
1239
+ document.documentElement.style.cursor = "crosshair";
1240
+ document.addEventListener("click", clickHandler, { capture: true });
1241
+ const panelUrl = `${SERVER_ORIGIN}/panel`;
1242
+ if (!activeContainer.isOpen()) {
1243
+ activeContainer.open(panelUrl);
1244
+ }
1245
+ } else {
1246
+ btn.classList.remove("active");
1247
+ document.documentElement.style.cursor = "";
1248
+ document.removeEventListener("click", clickHandler, { capture: true });
1249
+ activeContainer.close();
1250
+ revertPreview();
1251
+ clearHighlights();
1252
+ }
1253
+ }
1254
+ function showToast(message, duration = 3e3) {
1255
+ const toast = document.createElement("div");
1256
+ toast.className = "toast";
1257
+ toast.textContent = message;
1258
+ shadowRoot.appendChild(toast);
1259
+ requestAnimationFrame(() => toast.classList.add("visible"));
1260
+ setTimeout(() => {
1261
+ toast.classList.remove("visible");
1262
+ setTimeout(() => toast.remove(), 200);
1263
+ }, duration);
1264
+ }
1265
+ var designCanvasWrappers = [];
1266
+ function injectDesignCanvas(insertMode) {
1267
+ if (!currentTargetEl || !currentBoundary) {
1268
+ showToast("Select an element first");
1269
+ return;
1270
+ }
1271
+ clearHighlights();
1272
+ const targetEl = currentTargetEl;
1273
+ const wrapper = document.createElement("div");
1274
+ wrapper.setAttribute("data-tw-design-canvas", "true");
1275
+ wrapper.style.cssText = `
1276
+ border: 2px dashed #00848B;
1277
+ border-radius: 6px;
1278
+ background: #FAFBFB;
1279
+ position: relative;
1280
+ overflow: hidden;
1281
+ width: 100%;
1282
+ height: 400px;
1283
+ min-width: 300px;
1284
+ min-height: 200px;
1285
+ box-shadow: 0 4px 24px rgba(0,0,0,0.15);
1286
+ box-sizing: border-box;
1287
+ `;
1288
+ const iframe = document.createElement("iframe");
1289
+ iframe.src = `${SERVER_ORIGIN}/panel/?mode=design`;
1290
+ iframe.style.cssText = `
1291
+ width: 100%;
1292
+ height: 100%;
1293
+ border: none;
1294
+ display: block;
1295
+ `;
1296
+ wrapper.appendChild(iframe);
1297
+ const resizeHandle = document.createElement("div");
1298
+ resizeHandle.style.cssText = `
1299
+ position: absolute;
1300
+ bottom: 0;
1301
+ left: 0;
1302
+ right: 0;
1303
+ height: 8px;
1304
+ cursor: ns-resize;
1305
+ background: linear-gradient(transparent, rgba(0,132,139,0.06));
1306
+ display: flex;
1307
+ align-items: center;
1308
+ justify-content: center;
1309
+ `;
1310
+ const resizeBar = document.createElement("div");
1311
+ resizeBar.style.cssText = `
1312
+ width: 32px;
1313
+ height: 3px;
1314
+ border-radius: 2px;
1315
+ background: #DFE2E2;
1316
+ `;
1317
+ resizeHandle.appendChild(resizeBar);
1318
+ wrapper.appendChild(resizeHandle);
1319
+ let startY = 0;
1320
+ let startHeight = 0;
1321
+ const onResizeMove = (e) => {
1322
+ const delta = e.clientY - startY;
1323
+ const newHeight = Math.max(150, startHeight + delta);
1324
+ wrapper.style.height = `${newHeight}px`;
1325
+ };
1326
+ const onResizeUp = () => {
1327
+ document.removeEventListener("mousemove", onResizeMove);
1328
+ document.removeEventListener("mouseup", onResizeUp);
1329
+ document.documentElement.style.cursor = "";
1330
+ };
1331
+ resizeHandle.addEventListener("mousedown", (e) => {
1332
+ e.preventDefault();
1333
+ startY = e.clientY;
1334
+ startHeight = wrapper.offsetHeight;
1335
+ document.documentElement.style.cursor = "ns-resize";
1336
+ document.addEventListener("mousemove", onResizeMove);
1337
+ document.addEventListener("mouseup", onResizeUp);
1338
+ });
1339
+ const cornerHandle = document.createElement("div");
1340
+ cornerHandle.style.cssText = `
1341
+ position: absolute;
1342
+ bottom: 0;
1343
+ right: 0;
1344
+ width: 14px;
1345
+ height: 14px;
1346
+ cursor: nwse-resize;
1347
+ z-index: 5;
1348
+ `;
1349
+ const cornerDeco = document.createElement("div");
1350
+ cornerDeco.style.cssText = `
1351
+ position: absolute;
1352
+ bottom: 2px;
1353
+ right: 2px;
1354
+ width: 8px;
1355
+ height: 8px;
1356
+ border-right: 2px solid #DFE2E2;
1357
+ border-bottom: 2px solid #DFE2E2;
1358
+ `;
1359
+ cornerHandle.appendChild(cornerDeco);
1360
+ wrapper.appendChild(cornerHandle);
1361
+ let cornerStartX = 0;
1362
+ let cornerStartY = 0;
1363
+ let cornerStartWidth = 0;
1364
+ let cornerStartHeight = 0;
1365
+ const onCornerMove = (e) => {
1366
+ const dw = e.clientX - cornerStartX;
1367
+ const dh = e.clientY - cornerStartY;
1368
+ wrapper.style.width = `${Math.max(200, cornerStartWidth + dw)}px`;
1369
+ wrapper.style.height = `${Math.max(150, cornerStartHeight + dh)}px`;
1370
+ };
1371
+ const onCornerUp = () => {
1372
+ document.removeEventListener("mousemove", onCornerMove);
1373
+ document.removeEventListener("mouseup", onCornerUp);
1374
+ document.documentElement.style.cursor = "";
1375
+ };
1376
+ cornerHandle.addEventListener("mousedown", (e) => {
1377
+ e.preventDefault();
1378
+ cornerStartX = e.clientX;
1379
+ cornerStartY = e.clientY;
1380
+ cornerStartWidth = wrapper.offsetWidth;
1381
+ cornerStartHeight = wrapper.offsetHeight;
1382
+ document.documentElement.style.cursor = "nwse-resize";
1383
+ document.addEventListener("mousemove", onCornerMove);
1384
+ document.addEventListener("mouseup", onCornerUp);
1385
+ });
1386
+ switch (insertMode) {
1387
+ case "before":
1388
+ targetEl.insertAdjacentElement("beforebegin", wrapper);
1389
+ break;
1390
+ case "after":
1391
+ targetEl.insertAdjacentElement("afterend", wrapper);
1392
+ break;
1393
+ case "first-child":
1394
+ targetEl.insertAdjacentElement("afterbegin", wrapper);
1395
+ break;
1396
+ case "last-child":
1397
+ targetEl.appendChild(wrapper);
1398
+ break;
1399
+ default:
1400
+ targetEl.insertAdjacentElement("beforebegin", wrapper);
1401
+ }
1402
+ designCanvasWrappers.push(wrapper);
1403
+ iframe.addEventListener("load", () => {
1404
+ const contextMsg = {
1405
+ type: "ELEMENT_CONTEXT",
1406
+ componentName: currentBoundary?.componentName ?? "",
1407
+ instanceCount: currentEquivalentNodes.length,
1408
+ target: {
1409
+ tag: targetEl.tagName.toLowerCase(),
1410
+ classes: typeof targetEl.className === "string" ? targetEl.className : "",
1411
+ innerText: (targetEl.innerText || "").trim().slice(0, 60)
1412
+ },
1413
+ context: buildContext(targetEl, "", "", /* @__PURE__ */ new Map()),
1414
+ insertMode
1415
+ };
1416
+ let attempts = 0;
1417
+ const trySend = () => {
1418
+ sendTo("design", contextMsg);
1419
+ attempts++;
1420
+ if (attempts < 5) setTimeout(trySend, 300);
1421
+ };
1422
+ setTimeout(trySend, 200);
1423
+ });
1424
+ }
1425
+ function getDefaultContainer() {
1426
+ try {
1427
+ const stored = localStorage.getItem("tw-panel-container");
1428
+ if (stored && (stored === "modal" || stored === "popover" || stored === "sidebar" || stored === "popup")) {
1429
+ return stored;
1430
+ }
1431
+ } catch {
1432
+ }
1433
+ return "popover";
1434
+ }
1435
+ function init() {
1436
+ shadowHost = document.createElement("div");
1437
+ shadowHost.id = "tw-visual-editor-host";
1438
+ shadowHost.style.cssText = "position:fixed;z-index:2147483647;top:0;left:0;width:0;height:0;";
1439
+ document.body.appendChild(shadowHost);
1440
+ shadowRoot = shadowHost.attachShadow({ mode: "open" });
1441
+ const style = document.createElement("style");
1442
+ style.textContent = OVERLAY_CSS;
1443
+ shadowRoot.appendChild(style);
1444
+ containers = {
1445
+ popover: new PopoverContainer(shadowRoot),
1446
+ modal: new ModalContainer(shadowRoot),
1447
+ sidebar: new SidebarContainer(shadowRoot),
1448
+ popup: new PopupContainer()
1449
+ };
1450
+ activeContainer = containers[getDefaultContainer()];
1451
+ const btn = document.createElement("button");
1452
+ btn.className = "toggle-btn";
1453
+ btn.textContent = "\u2295";
1454
+ btn.addEventListener("click", () => toggleInspect(btn));
1455
+ shadowRoot.appendChild(btn);
1456
+ const wsUrl = SERVER_ORIGIN.replace(/^http/, "ws");
1457
+ connect(wsUrl);
1458
+ onMessage((msg) => {
1459
+ if (msg.type === "PATCH_PREVIEW" && currentEquivalentNodes.length > 0) {
1460
+ applyPreview(currentEquivalentNodes, msg.oldClass, msg.newClass, SERVER_ORIGIN);
1461
+ } else if (msg.type === "PATCH_REVERT") {
1462
+ revertPreview();
1463
+ } else if (msg.type === "PATCH_STAGE" && currentTargetEl && currentBoundary) {
1464
+ const state = getPreviewState();
1465
+ const originalClassMap = /* @__PURE__ */ new Map();
1466
+ if (state) {
1467
+ for (let i = 0; i < state.elements.length; i++) {
1468
+ originalClassMap.set(state.elements[i], state.originalClasses[i]);
1469
+ }
1470
+ }
1471
+ const targetElIndex = currentEquivalentNodes.indexOf(currentTargetEl);
1472
+ const originalClassString = state && targetElIndex !== -1 ? state.originalClasses[targetElIndex] : currentTargetEl.className;
1473
+ const context = buildContext(currentTargetEl, msg.oldClass, msg.newClass, originalClassMap);
1474
+ send({
1475
+ type: "PATCH_STAGED",
1476
+ patch: {
1477
+ id: msg.id,
1478
+ elementKey: currentBoundary.componentName,
1479
+ status: "staged",
1480
+ originalClass: msg.oldClass,
1481
+ newClass: msg.newClass,
1482
+ property: msg.property,
1483
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1484
+ pageUrl: window.location.href,
1485
+ component: { name: currentBoundary.componentName },
1486
+ target: {
1487
+ tag: currentTargetEl.tagName.toLowerCase(),
1488
+ classes: originalClassString,
1489
+ innerText: (currentTargetEl.innerText || "").trim().slice(0, 60)
1490
+ },
1491
+ context
1492
+ }
1493
+ });
1494
+ showToast("Change staged");
1495
+ } else if (msg.type === "CLEAR_HIGHLIGHTS") {
1496
+ clearHighlights();
1497
+ } else if (msg.type === "SWITCH_CONTAINER") {
1498
+ const newName = msg.container;
1499
+ if (containers[newName] && newName !== activeContainer.name) {
1500
+ const wasOpen = activeContainer.isOpen();
1501
+ activeContainer.close();
1502
+ activeContainer = containers[newName];
1503
+ if (wasOpen) {
1504
+ activeContainer.open(`${SERVER_ORIGIN}/panel`);
1505
+ }
1506
+ }
1507
+ } else if (msg.type === "INSERT_DESIGN_CANVAS") {
1508
+ injectDesignCanvas(msg.insertMode);
1509
+ } else if (msg.type === "DESIGN_SUBMITTED") {
1510
+ const last = designCanvasWrappers[designCanvasWrappers.length - 1];
1511
+ if (last) {
1512
+ const iframe = last.querySelector("iframe");
1513
+ if (iframe && msg.image) {
1514
+ const img = document.createElement("img");
1515
+ img.src = msg.image;
1516
+ img.style.cssText = `
1517
+ width: 100%;
1518
+ height: auto;
1519
+ display: block;
1520
+ pointer-events: none;
1521
+ `;
1522
+ last.innerHTML = "";
1523
+ last.style.height = "auto";
1524
+ last.style.minHeight = "0";
1525
+ last.style.overflow = "hidden";
1526
+ last.appendChild(img);
1527
+ }
1528
+ }
1529
+ } else if (msg.type === "DESIGN_CLOSE") {
1530
+ const last = designCanvasWrappers.pop();
1531
+ if (last) last.remove();
1532
+ }
1533
+ });
1534
+ window.addEventListener("overlay-ws-connected", () => {
1535
+ if (wasConnected) {
1536
+ showToast("Reconnected");
1537
+ }
1538
+ wasConnected = true;
1539
+ });
1540
+ window.addEventListener("overlay-ws-disconnected", () => {
1541
+ if (wasConnected) {
1542
+ showToast("Connection lost \u2014 restart the server and refresh.", 5e3);
1543
+ }
1544
+ });
1545
+ }
1546
+ init();
1547
+ })();