@base44/vite-plugin 0.2.24 → 0.2.26

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.
Files changed (38) hide show
  1. package/dist/injections/layer-dropdown/consts.d.ts +19 -0
  2. package/dist/injections/layer-dropdown/consts.d.ts.map +1 -0
  3. package/dist/injections/layer-dropdown/consts.js +40 -0
  4. package/dist/injections/layer-dropdown/consts.js.map +1 -0
  5. package/dist/injections/layer-dropdown/controller.d.ts +4 -0
  6. package/dist/injections/layer-dropdown/controller.d.ts.map +1 -0
  7. package/dist/injections/layer-dropdown/controller.js +88 -0
  8. package/dist/injections/layer-dropdown/controller.js.map +1 -0
  9. package/dist/injections/layer-dropdown/dropdown-ui.d.ts +13 -0
  10. package/dist/injections/layer-dropdown/dropdown-ui.d.ts.map +1 -0
  11. package/dist/injections/layer-dropdown/dropdown-ui.js +176 -0
  12. package/dist/injections/layer-dropdown/dropdown-ui.js.map +1 -0
  13. package/dist/injections/layer-dropdown/types.d.ts +26 -0
  14. package/dist/injections/layer-dropdown/types.d.ts.map +1 -0
  15. package/dist/injections/layer-dropdown/types.js +3 -0
  16. package/dist/injections/layer-dropdown/types.js.map +1 -0
  17. package/dist/injections/layer-dropdown/utils.d.ts +25 -0
  18. package/dist/injections/layer-dropdown/utils.d.ts.map +1 -0
  19. package/dist/injections/layer-dropdown/utils.js +143 -0
  20. package/dist/injections/layer-dropdown/utils.js.map +1 -0
  21. package/dist/injections/utils.d.ts +9 -0
  22. package/dist/injections/utils.d.ts.map +1 -1
  23. package/dist/injections/utils.js +33 -0
  24. package/dist/injections/utils.js.map +1 -1
  25. package/dist/injections/visual-edit-agent.d.ts.map +1 -1
  26. package/dist/injections/visual-edit-agent.js +115 -69
  27. package/dist/injections/visual-edit-agent.js.map +1 -1
  28. package/dist/statics/index.mjs +1 -1
  29. package/dist/statics/index.mjs.map +1 -1
  30. package/package.json +1 -1
  31. package/src/injections/layer-dropdown/LAYERS.md +258 -0
  32. package/src/injections/layer-dropdown/consts.ts +49 -0
  33. package/src/injections/layer-dropdown/controller.ts +109 -0
  34. package/src/injections/layer-dropdown/dropdown-ui.ts +230 -0
  35. package/src/injections/layer-dropdown/types.ts +30 -0
  36. package/src/injections/layer-dropdown/utils.ts +175 -0
  37. package/src/injections/utils.ts +44 -1
  38. package/src/injections/visual-edit-agent.ts +141 -80
@@ -1,4 +1,6 @@
1
- import { findElementsById, updateElementClasses } from "./utils.js";
1
+ import { findElementsById, updateElementClasses, updateElementAttribute, collectAllowedAttributes, ALLOWED_ATTRIBUTES, getElementSelectorId } from "./utils.js";
2
+ import { createLayerController } from "./layer-dropdown/controller.js";
3
+ import { LAYER_DROPDOWN_ATTR } from "./layer-dropdown/consts.js";
2
4
 
3
5
  export function setupVisualEditAgent() {
4
6
  // State variables (replacing React useState/useRef)
@@ -78,6 +80,76 @@ export function setupVisualEditAgent() {
78
80
  currentHighlightedElements = [];
79
81
  };
80
82
 
83
+ const clearSelectedOverlays = () => {
84
+ selectedOverlays.forEach((overlay) => {
85
+ if (overlay && overlay.parentNode) {
86
+ overlay.remove();
87
+ }
88
+ });
89
+ selectedOverlays = [];
90
+ };
91
+
92
+ const TEXT_TAGS = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'a', 'label'];
93
+
94
+ const notifyElementSelected = (element: Element) => {
95
+ const htmlElement = element as HTMLElement;
96
+ const rect = element.getBoundingClientRect();
97
+ const svgElement = element as SVGElement;
98
+ const isTextElement = TEXT_TAGS.includes(element.tagName?.toLowerCase());
99
+ window.parent.postMessage({
100
+ type: "element-selected",
101
+ tagName: element.tagName,
102
+ classes:
103
+ (svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
104
+ element.className ||
105
+ "",
106
+ visualSelectorId: getElementSelectorId(element),
107
+ content: isTextElement ? htmlElement.innerText : undefined,
108
+ dataSourceLocation: htmlElement.dataset.sourceLocation,
109
+ isDynamicContent: htmlElement.dataset.dynamicContent === "true",
110
+ linenumber: htmlElement.dataset.linenumber,
111
+ filename: htmlElement.dataset.filename,
112
+ position: {
113
+ top: rect.top,
114
+ left: rect.left,
115
+ right: rect.right,
116
+ bottom: rect.bottom,
117
+ width: rect.width,
118
+ height: rect.height,
119
+ centerX: rect.left + rect.width / 2,
120
+ centerY: rect.top + rect.height / 2,
121
+ },
122
+ attributes: collectAllowedAttributes(element, ALLOWED_ATTRIBUTES),
123
+ isTextElement,
124
+ }, "*");
125
+ };
126
+
127
+ // Select an element: create overlays, update state, notify parent
128
+ const selectElement = (element: Element): HTMLDivElement | undefined => {
129
+ const visualSelectorId = getElementSelectorId(element);
130
+
131
+ clearSelectedOverlays();
132
+
133
+ const elements = findElementsById(visualSelectorId || null);
134
+ elements.forEach((el) => {
135
+ const overlay = createOverlay(true);
136
+ document.body.appendChild(overlay);
137
+ selectedOverlays.push(overlay);
138
+ positionOverlay(overlay, el, true);
139
+ });
140
+
141
+ selectedElementId = visualSelectorId || null;
142
+ clearHoverOverlays();
143
+ notifyElementSelected(element);
144
+
145
+ return selectedOverlays[0];
146
+ };
147
+
148
+ const notifyDeselection = (): void => {
149
+ selectedElementId = null;
150
+ window.parent.postMessage({ type: "unselect-element" }, "*");
151
+ };
152
+
81
153
  // Handle mouse over event
82
154
  const handleMouseOver = (e: MouseEvent) => {
83
155
  if (!isVisualEditMode || isPopoverDragging) return;
@@ -146,6 +218,9 @@ export function setupVisualEditAgent() {
146
218
 
147
219
  const target = e.target as Element;
148
220
 
221
+ // Let layer dropdown clicks pass through without interference
222
+ if (target.closest(`[${LAYER_DROPDOWN_ATTR}]`)) return;
223
+
149
224
  // Close dropdowns when clicking anywhere in iframe if a dropdown is open
150
225
  if (isDropdownOpen) {
151
226
  e.preventDefault();
@@ -174,79 +249,13 @@ export function setupVisualEditAgent() {
174
249
  return;
175
250
  }
176
251
 
177
- const htmlElement = element as HTMLElement;
178
- const visualSelectorId =
179
- htmlElement.dataset.sourceLocation ||
180
- htmlElement.dataset.visualSelectorId;
181
-
182
- // Clear any existing selected overlays
183
- selectedOverlays.forEach((overlay) => {
184
- if (overlay && overlay.parentNode) {
185
- overlay.remove();
186
- }
187
- });
188
- selectedOverlays = [];
189
-
190
- // Find all elements with the same ID
191
- const elements = findElementsById(visualSelectorId || null);
192
-
193
- // Create selected overlays for all matching elements
194
- elements.forEach((el) => {
195
- const overlay = createOverlay(true);
196
- document.body.appendChild(overlay);
197
- selectedOverlays.push(overlay);
198
- positionOverlay(overlay, el, true);
199
- });
200
-
201
- selectedElementId = visualSelectorId || null;
202
-
203
- // Clear hover overlays
204
- clearHoverOverlays();
205
-
206
- // Calculate element position for popover positioning
207
- const rect = element.getBoundingClientRect();
208
- const elementPosition = {
209
- top: rect.top,
210
- left: rect.left,
211
- right: rect.right,
212
- bottom: rect.bottom,
213
- width: rect.width,
214
- height: rect.height,
215
- centerX: rect.left + rect.width / 2,
216
- centerY: rect.top + rect.height / 2,
217
- };
218
-
219
- const isTextElement = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'a', 'label'].includes(element.tagName?.toLowerCase())
220
-
221
- // Send message to parent window with element info including position
222
- const svgElement = element as SVGElement;
223
- const elementData = {
224
- type: "element-selected",
225
- tagName: element.tagName,
226
- classes:
227
- (svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
228
- element.className ||
229
- "",
230
- visualSelectorId: visualSelectorId,
231
- content: isTextElement ? (element as HTMLElement).innerText : undefined,
232
- dataSourceLocation: htmlElement.dataset.sourceLocation,
233
- isDynamicContent: htmlElement.dataset.dynamicContent === "true",
234
- linenumber: htmlElement.dataset.linenumber,
235
- filename: htmlElement.dataset.filename,
236
- position: elementPosition,
237
- isTextElement: isTextElement,
238
- };
239
- window.parent.postMessage(elementData, "*");
252
+ const selectedOverlay = selectElement(element);
253
+ layerController.attachToOverlay(selectedOverlay, element);
240
254
  };
241
255
 
242
- // Unselect the current element
243
- const unselectElement = () => {
244
- selectedOverlays.forEach((overlay) => {
245
- if (overlay && overlay.parentNode) {
246
- overlay.remove();
247
- }
248
- });
249
- selectedOverlays = [];
256
+ // Clear the current selection
257
+ const clearSelection = () => {
258
+ clearSelectedOverlays();
250
259
  selectedElementId = null;
251
260
  };
252
261
 
@@ -282,6 +291,29 @@ export function setupVisualEditAgent() {
282
291
  }, 50);
283
292
  };
284
293
 
294
+ // Update element attribute by visual selector ID
295
+ const updateElementAttributeAndReposition = (
296
+ visualSelectorId: string,
297
+ attribute: string,
298
+ value: string
299
+ ) => {
300
+ const elements = findElementsById(visualSelectorId);
301
+ if (elements.length === 0) return;
302
+
303
+ updateElementAttribute(elements, attribute, value);
304
+
305
+ // Reposition overlays after attribute change (e.g. image src swap can affect layout)
306
+ setTimeout(() => {
307
+ if (selectedElementId === visualSelectorId) {
308
+ selectedOverlays.forEach((overlay, index) => {
309
+ if (index < elements.length) {
310
+ positionOverlay(overlay, elements[index]!);
311
+ }
312
+ });
313
+ }
314
+ }, 50);
315
+ };
316
+
285
317
  // Update element content by visual selector ID
286
318
  const updateElementContent = (visualSelectorId: string, content: string) => {
287
319
  const elements = findElementsById(visualSelectorId);
@@ -305,19 +337,28 @@ export function setupVisualEditAgent() {
305
337
  }, 50);
306
338
  };
307
339
 
340
+ // --- Layer dropdown controller ---
341
+ const layerController = createLayerController({
342
+ createPreviewOverlay: (element: Element) => {
343
+ const overlay = createOverlay(false);
344
+ overlay.style.zIndex = "9998";
345
+ document.body.appendChild(overlay);
346
+ positionOverlay(overlay, element);
347
+ return overlay;
348
+ },
349
+ getSelectedElementId: () => selectedElementId,
350
+ selectElement,
351
+ onDeselect: notifyDeselection,
352
+ });
353
+
308
354
  // Toggle visual edit mode
309
355
  const toggleVisualEditMode = (isEnabled: boolean) => {
310
356
  isVisualEditMode = isEnabled;
311
357
 
312
358
  if (!isEnabled) {
359
+ layerController.cleanup();
313
360
  clearHoverOverlays();
314
-
315
- selectedOverlays.forEach((overlay) => {
316
- if (overlay && overlay.parentNode) {
317
- overlay.remove();
318
- }
319
- });
320
- selectedOverlays = [];
361
+ clearSelectedOverlays();
321
362
 
322
363
  currentHighlightedElements = [];
323
364
  selectedElementId = null;
@@ -397,8 +438,28 @@ export function setupVisualEditAgent() {
397
438
  }
398
439
  break;
399
440
 
441
+ case "update-attribute":
442
+ if (
443
+ message.data &&
444
+ message.data.visualSelectorId &&
445
+ message.data.attribute !== undefined &&
446
+ message.data.value !== undefined
447
+ ) {
448
+ updateElementAttributeAndReposition(
449
+ message.data.visualSelectorId,
450
+ message.data.attribute,
451
+ message.data.value
452
+ );
453
+ } else {
454
+ console.warn(
455
+ "[VisualEditAgent] Invalid update-attribute message:",
456
+ message
457
+ );
458
+ }
459
+ break;
460
+
400
461
  case "unselect-element":
401
- unselectElement();
462
+ clearSelection();
402
463
  break;
403
464
 
404
465
  case "refresh-page":