@base44-preview/vite-plugin 0.2.29-pr.45.28c5745 → 0.2.29-pr.45.b9c5a5a

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.
@@ -18,6 +18,8 @@ export function getElementSelectorId(element: Element): string | null {
18
18
 
19
19
  export const ALLOWED_ATTRIBUTES: string[] = ["src"];
20
20
 
21
+ export const PLUGIN_ELEMENT_ATTR = "data-vite-plugin-element";
22
+
21
23
  /** Find elements by ID - first try data-source-location, fallback to data-visual-selector-id */
22
24
  export function findElementsById(id: string | null): Element[] {
23
25
  if (!id) return [];
@@ -65,67 +67,103 @@ export function collectAllowedAttributes(element: Element, allowedAttributes: st
65
67
  return attributes;
66
68
  }
67
69
 
68
- let isFrozen = false;
69
-
70
+ /**
71
+ * Freeze all CSS animations and transitions on the page by injecting
72
+ * scoped styles under `[data-visual-edit-active]` and programmatically
73
+ * finishing (or pausing) every running animation.
74
+ *
75
+ * Plugin-owned elements (`[data-vite-plugin-element]`) are excluded so
76
+ * the plugin UI stays animated.
77
+ */
70
78
  export function stopAnimations(): void {
71
- if (isFrozen) return;
72
- isFrozen = true;
73
-
74
- document.getElementById('freeze-styles')?.remove();
75
- const style = document.createElement('style');
76
- style.id = 'freeze-styles';
77
- style.textContent = `
78
- *, *::before, *::after {
79
+ if (document.getElementById('freeze-animations')) return;
80
+
81
+ document.documentElement.setAttribute('data-visual-edit-active', '');
82
+
83
+ const animStyle = document.createElement('style');
84
+ animStyle.id = 'freeze-animations';
85
+ animStyle.textContent = `
86
+ [data-visual-edit-active] *:not([${PLUGIN_ELEMENT_ATTR}]):not([${PLUGIN_ELEMENT_ATTR}] *),
87
+ [data-visual-edit-active] *:not([${PLUGIN_ELEMENT_ATTR}]):not([${PLUGIN_ELEMENT_ATTR}] *)::before,
88
+ [data-visual-edit-active] *:not([${PLUGIN_ELEMENT_ATTR}]):not([${PLUGIN_ELEMENT_ATTR}] *)::after {
79
89
  animation-play-state: paused !important;
80
90
  transition: none !important;
81
91
  }
82
- body * {
83
- pointer-events: none !important;
84
- }
85
- [data-layer-dropdown],
86
- [data-layer-dropdown] * {
87
- pointer-events: auto !important;
88
- }
89
- [contenteditable="true"] {
90
- pointer-events: auto !important;
91
- }
92
92
  `;
93
- (document.head || document.documentElement).appendChild(style);
94
93
 
95
- document.getAnimations().forEach((a) => a.pause());
94
+ const pointerStyle = document.createElement('style');
95
+ pointerStyle.id = 'freeze-pointer-events';
96
+ pointerStyle.textContent = `
97
+ [data-visual-edit-active] * { pointer-events: none !important; }
98
+ [${PLUGIN_ELEMENT_ATTR}], [${PLUGIN_ELEMENT_ATTR}] * { pointer-events: auto !important; }
99
+ `;
100
+
101
+ const target = document.head || document.documentElement;
102
+ target.appendChild(animStyle);
103
+ target.appendChild(pointerStyle);
104
+
105
+ document.getAnimations().forEach((a) => {
106
+ // Skip animations on plugin UI elements
107
+ const animTarget = (a.effect as KeyframeEffect)?.target;
108
+ if (animTarget instanceof Element && animTarget.closest(`[${PLUGIN_ELEMENT_ATTR}]`)) return;
109
+
110
+ try {
111
+ a.finish(); // fast-forward to end state
112
+ } catch {
113
+ a.pause(); // finish() throws on infinite animations — pause instead
114
+ }
115
+ });
96
116
  }
97
117
 
118
+ /**
119
+ * Resume all previously frozen animations and remove the injected
120
+ * freeze styles. Cleans up the `data-visual-edit-active` attribute
121
+ * from `<html>` so scoped selectors no longer match.
122
+ */
98
123
  export function resumeAnimations(): void {
99
- if (!isFrozen) return;
100
- isFrozen = false;
124
+ const animStyle = document.getElementById('freeze-animations');
125
+ if (!animStyle) return;
101
126
 
102
- document.getElementById('freeze-styles')?.remove();
127
+ animStyle.remove();
128
+ document.getElementById('freeze-pointer-events')?.remove();
129
+ document.documentElement.removeAttribute('data-visual-edit-active');
103
130
 
104
131
  document.getAnimations().forEach((a) => {
105
- try { a.play(); } catch { /* animation target may have been removed */ }
132
+ if (a.playState === 'paused') {
133
+ try { a.play(); } catch { /* animation target may have been removed */ }
134
+ }
106
135
  });
107
136
  }
108
137
 
138
+ /**
139
+ * Hit-test the page at (`x`, `y`) and walk up the DOM to find the
140
+ * nearest ancestor that carries instrumentation attributes
141
+ * (`data-source-location` or `data-visual-selector-id`).
142
+ *
143
+ * Temporarily disables the pointer-events freeze sheet so the
144
+ * browser's native `elementFromPoint` can reach the real target.
145
+ */
109
146
  export function findInstrumentedElement(x: number, y: number): Element | null {
110
- const freezeStyle = document.getElementById('freeze-styles');
111
- if (freezeStyle) freezeStyle.remove();
147
+ const pointerStyle = document.getElementById('freeze-pointer-events') as HTMLStyleElement | null;
148
+ if (pointerStyle) pointerStyle.disabled = true;
112
149
 
113
150
  const el = document.elementFromPoint(x, y);
114
151
 
115
- if (freezeStyle) (document.head || document.documentElement).appendChild(freezeStyle);
152
+ if (pointerStyle) pointerStyle.disabled = false;
116
153
 
117
154
  return el?.closest('[data-source-location], [data-visual-selector-id]') ?? null;
118
155
  }
119
156
 
120
- /** Resolve which element should be hovered at (x, y), skipping the currently selected element. */
157
+ /**
158
+ * Resolve which element should be hovered at (`x`, `y`), skipping the
159
+ * currently selected element. Returns the selector ID of the hovered
160
+ * element, or `null` if the point is empty or hits the selected element.
161
+ */
121
162
  export function resolveHoverTarget(x: number, y: number, selectedElementId: string | null): string | null {
122
163
  const element = findInstrumentedElement(x, y);
123
164
  if (!element) return null;
124
165
 
125
- const htmlElement = element as HTMLElement;
126
- const selectorId =
127
- htmlElement.dataset.sourceLocation ||
128
- htmlElement.dataset.visualSelectorId || null;
166
+ const selectorId = getElementSelectorId(element);
129
167
 
130
168
  if (selectorId === selectedElementId) return null;
131
169
 
@@ -196,10 +196,8 @@ export function setupVisualEditAgent() {
196
196
  let pendingMouseMoveRaf: number | null = null;
197
197
 
198
198
  const clearHoverState = () => {
199
- if (lastHoveredSelectorId !== null) {
200
- clearHoverOverlays();
201
- lastHoveredSelectorId = null;
202
- }
199
+ clearHoverOverlays();
200
+ lastHoveredSelectorId = null;
203
201
  };
204
202
 
205
203
  const applyHoverOverlays = (selectorId: string) => {
@@ -240,8 +238,7 @@ export function setupVisualEditAgent() {
240
238
  cancelAnimationFrame(pendingMouseMoveRaf);
241
239
  pendingMouseMoveRaf = null;
242
240
  }
243
- clearHoverOverlays();
244
- lastHoveredSelectorId = null;
241
+ clearHoverState();
245
242
  };
246
243
 
247
244
  // Handle element click
@@ -421,14 +418,7 @@ export function setupVisualEditAgent() {
421
418
  inlineEdit.stopEditing();
422
419
  clearSelection();
423
420
  layerController.cleanup();
424
- clearHoverOverlays();
425
-
426
- currentHighlightedElements = [];
427
- lastHoveredSelectorId = null;
428
- if (pendingMouseMoveRaf !== null) {
429
- cancelAnimationFrame(pendingMouseMoveRaf);
430
- pendingMouseMoveRaf = null;
431
- }
421
+ handleMouseLeave();
432
422
  document.body.style.cursor = "default";
433
423
 
434
424
  document.removeEventListener("mousemove", handleMouseMove);