@depup/base44__vite-plugin 1.0.4-depup.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.
- package/README.md +34 -0
- package/changes.json +22 -0
- package/compat/agents.cjs +13 -0
- package/compat/base44Client.cjs +6 -0
- package/compat/entities.cjs +25 -0
- package/compat/functions.cjs +9 -0
- package/compat/integrations.cjs +9 -0
- package/dist/ErrorOverlay.d.ts +12 -0
- package/dist/ErrorOverlay.d.ts.map +1 -0
- package/dist/ErrorOverlay.js +51 -0
- package/dist/ErrorOverlay.js.map +1 -0
- package/dist/bridge.d.ts +8 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +8 -0
- package/dist/bridge.js.map +1 -0
- package/dist/capabilities/inline-edit/controller.d.ts +3 -0
- package/dist/capabilities/inline-edit/controller.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/controller.js +203 -0
- package/dist/capabilities/inline-edit/controller.js.map +1 -0
- package/dist/capabilities/inline-edit/dom-utils.d.ts +7 -0
- package/dist/capabilities/inline-edit/dom-utils.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/dom-utils.js +59 -0
- package/dist/capabilities/inline-edit/dom-utils.js.map +1 -0
- package/dist/capabilities/inline-edit/index.d.ts +3 -0
- package/dist/capabilities/inline-edit/index.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/index.js +2 -0
- package/dist/capabilities/inline-edit/index.js.map +1 -0
- package/dist/capabilities/inline-edit/types.d.ts +29 -0
- package/dist/capabilities/inline-edit/types.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/types.js +2 -0
- package/dist/capabilities/inline-edit/types.js.map +1 -0
- package/dist/consts.d.ts +11 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/consts.js +11 -0
- package/dist/consts.js.map +1 -0
- package/dist/error-overlay-plugin.d.ts +3 -0
- package/dist/error-overlay-plugin.d.ts.map +1 -0
- package/dist/error-overlay-plugin.js +15 -0
- package/dist/error-overlay-plugin.js.map +1 -0
- package/dist/html-injections-plugin.d.ts +8 -0
- package/dist/html-injections-plugin.d.ts.map +1 -0
- package/dist/html-injections-plugin.js +132 -0
- package/dist/html-injections-plugin.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/dist/injections/layer-dropdown/consts.d.ts +20 -0
- package/dist/injections/layer-dropdown/consts.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/consts.js +41 -0
- package/dist/injections/layer-dropdown/consts.js.map +1 -0
- package/dist/injections/layer-dropdown/controller.d.ts +4 -0
- package/dist/injections/layer-dropdown/controller.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/controller.js +88 -0
- package/dist/injections/layer-dropdown/controller.js.map +1 -0
- package/dist/injections/layer-dropdown/dropdown-ui.d.ts +13 -0
- package/dist/injections/layer-dropdown/dropdown-ui.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/dropdown-ui.js +186 -0
- package/dist/injections/layer-dropdown/dropdown-ui.js.map +1 -0
- package/dist/injections/layer-dropdown/types.d.ts +26 -0
- package/dist/injections/layer-dropdown/types.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/types.js +3 -0
- package/dist/injections/layer-dropdown/types.js.map +1 -0
- package/dist/injections/layer-dropdown/utils.d.ts +25 -0
- package/dist/injections/layer-dropdown/utils.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/utils.js +143 -0
- package/dist/injections/layer-dropdown/utils.js.map +1 -0
- package/dist/injections/navigation-notifier.d.ts +2 -0
- package/dist/injections/navigation-notifier.d.ts.map +1 -0
- package/dist/injections/navigation-notifier.js +34 -0
- package/dist/injections/navigation-notifier.js.map +1 -0
- package/dist/injections/sandbox-hmr-notifier.d.ts +2 -0
- package/dist/injections/sandbox-hmr-notifier.d.ts.map +1 -0
- package/dist/injections/sandbox-hmr-notifier.js +10 -0
- package/dist/injections/sandbox-hmr-notifier.js.map +1 -0
- package/dist/injections/sandbox-mount-observer.d.ts +2 -0
- package/dist/injections/sandbox-mount-observer.d.ts.map +1 -0
- package/dist/injections/sandbox-mount-observer.js +18 -0
- package/dist/injections/sandbox-mount-observer.js.map +1 -0
- package/dist/injections/unhandled-errors-handlers.d.ts +2 -0
- package/dist/injections/unhandled-errors-handlers.d.ts.map +1 -0
- package/dist/injections/unhandled-errors-handlers.js +93 -0
- package/dist/injections/unhandled-errors-handlers.js.map +1 -0
- package/dist/injections/utils.d.ts +65 -0
- package/dist/injections/utils.d.ts.map +1 -0
- package/dist/injections/utils.js +186 -0
- package/dist/injections/utils.js.map +1 -0
- package/dist/injections/visual-edit-agent.d.ts +2 -0
- package/dist/injections/visual-edit-agent.d.ts.map +1 -0
- package/dist/injections/visual-edit-agent.js +583 -0
- package/dist/injections/visual-edit-agent.js.map +1 -0
- package/dist/jsx-processor.d.ts +17 -0
- package/dist/jsx-processor.d.ts.map +1 -0
- package/dist/jsx-processor.js +129 -0
- package/dist/jsx-processor.js.map +1 -0
- package/dist/jsx-utils.d.ts +16 -0
- package/dist/jsx-utils.d.ts.map +1 -0
- package/dist/jsx-utils.js +98 -0
- package/dist/jsx-utils.js.map +1 -0
- package/dist/processors/collection-id-processor.d.ts +20 -0
- package/dist/processors/collection-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-id-processor.js +182 -0
- package/dist/processors/collection-id-processor.js.map +1 -0
- package/dist/processors/collection-item-field-processor.d.ts +39 -0
- package/dist/processors/collection-item-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-field-processor.js +289 -0
- package/dist/processors/collection-item-field-processor.js.map +1 -0
- package/dist/processors/collection-item-id-processor.d.ts +12 -0
- package/dist/processors/collection-item-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-id-processor.js +50 -0
- package/dist/processors/collection-item-id-processor.js.map +1 -0
- package/dist/processors/static-array-processor.d.ts +28 -0
- package/dist/processors/static-array-processor.d.ts.map +1 -0
- package/dist/processors/static-array-processor.js +173 -0
- package/dist/processors/static-array-processor.js.map +1 -0
- package/dist/processors/utils/collection-tracing-utils.d.ts +36 -0
- package/dist/processors/utils/collection-tracing-utils.d.ts.map +1 -0
- package/dist/processors/utils/collection-tracing-utils.js +390 -0
- package/dist/processors/utils/collection-tracing-utils.js.map +1 -0
- package/dist/processors/utils/shared-utils.d.ts +96 -0
- package/dist/processors/utils/shared-utils.d.ts.map +1 -0
- package/dist/processors/utils/shared-utils.js +600 -0
- package/dist/processors/utils/shared-utils.js.map +1 -0
- package/dist/statics/index.mjs +16 -0
- package/dist/statics/index.mjs.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +22 -0
- package/dist/utils.js.map +1 -0
- package/dist/visual-edit-plugin.d.ts +3 -0
- package/dist/visual-edit-plugin.d.ts.map +1 -0
- package/dist/visual-edit-plugin.js +100 -0
- package/dist/visual-edit-plugin.js.map +1 -0
- package/package.json +75 -0
- package/src/ErrorOverlay.ts +71 -0
- package/src/bridge.ts +8 -0
- package/src/capabilities/inline-edit/controller.ts +254 -0
- package/src/capabilities/inline-edit/dom-utils.ts +58 -0
- package/src/capabilities/inline-edit/index.ts +2 -0
- package/src/capabilities/inline-edit/types.ts +35 -0
- package/src/consts.ts +11 -0
- package/src/error-overlay-plugin.ts +19 -0
- package/src/html-injections-plugin.ts +166 -0
- package/src/index.ts +225 -0
- package/src/injections/layer-dropdown/LAYERS.md +258 -0
- package/src/injections/layer-dropdown/consts.ts +51 -0
- package/src/injections/layer-dropdown/controller.ts +109 -0
- package/src/injections/layer-dropdown/dropdown-ui.ts +242 -0
- package/src/injections/layer-dropdown/types.ts +30 -0
- package/src/injections/layer-dropdown/utils.ts +175 -0
- package/src/injections/navigation-notifier.ts +43 -0
- package/src/injections/sandbox-hmr-notifier.ts +8 -0
- package/src/injections/sandbox-mount-observer.ts +25 -0
- package/src/injections/unhandled-errors-handlers.ts +114 -0
- package/src/injections/utils.ts +208 -0
- package/src/injections/visual-edit-agent.ts +706 -0
- package/src/jsx-processor.ts +169 -0
- package/src/jsx-utils.ts +131 -0
- package/src/processors/collection-id-processor.ts +261 -0
- package/src/processors/collection-item-field-processor.ts +439 -0
- package/src/processors/collection-item-id-processor.ts +69 -0
- package/src/processors/static-array-processor.ts +260 -0
- package/src/processors/utils/collection-tracing-utils.ts +507 -0
- package/src/processors/utils/shared-utils.ts +785 -0
- package/src/utils.ts +27 -0
- package/src/visual-edit-plugin.md +358 -0
- package/src/visual-edit-plugin.ts +110 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import type { InlineEditHost, InlineEditController } from "./types.js";
|
|
2
|
+
import {
|
|
3
|
+
injectFocusOutlineCSS,
|
|
4
|
+
removeFocusOutlineCSS,
|
|
5
|
+
selectText,
|
|
6
|
+
shouldEnterInlineEditingMode,
|
|
7
|
+
isStaticArrayTextElement,
|
|
8
|
+
} from "./dom-utils.js";
|
|
9
|
+
import { PLUGIN_ELEMENT_ATTR } from "../../injections/utils.js";
|
|
10
|
+
|
|
11
|
+
const DEBOUNCE_MS = 500;
|
|
12
|
+
|
|
13
|
+
export function createInlineEditController(
|
|
14
|
+
host: InlineEditHost
|
|
15
|
+
): InlineEditController {
|
|
16
|
+
let currentEditingElement: HTMLElement | null = null;
|
|
17
|
+
let debouncedSendTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
18
|
+
let enabled = false;
|
|
19
|
+
const listenerAbortControllers = new WeakMap<HTMLElement, AbortController>();
|
|
20
|
+
|
|
21
|
+
// --- Private helpers ---
|
|
22
|
+
|
|
23
|
+
const repositionOverlays = () => {
|
|
24
|
+
const selectedId = host.getSelectedElementId();
|
|
25
|
+
if (!selectedId) return;
|
|
26
|
+
const elements = host.findElementsById(selectedId);
|
|
27
|
+
const overlays = host.getSelectedOverlays();
|
|
28
|
+
overlays.forEach((overlay, i) => {
|
|
29
|
+
if (i < elements.length && elements[i]) {
|
|
30
|
+
host.positionOverlay(overlay, elements[i]);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const reportEdit = (element: HTMLElement) => {
|
|
36
|
+
const originalContent = element.dataset.originalTextContent;
|
|
37
|
+
const newContent = element.textContent;
|
|
38
|
+
|
|
39
|
+
const svgElement = element as unknown as SVGElement;
|
|
40
|
+
const rect = element.getBoundingClientRect();
|
|
41
|
+
|
|
42
|
+
const message: Record<string, unknown> = {
|
|
43
|
+
type: "inline-edit",
|
|
44
|
+
elementInfo: {
|
|
45
|
+
tagName: element.tagName,
|
|
46
|
+
classes:
|
|
47
|
+
(svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
|
|
48
|
+
element.className ||
|
|
49
|
+
"",
|
|
50
|
+
visualSelectorId: host.getSelectedElementId(),
|
|
51
|
+
content: newContent,
|
|
52
|
+
dataSourceLocation: element.dataset.sourceLocation,
|
|
53
|
+
isDynamicContent: element.dataset.dynamicContent === "true",
|
|
54
|
+
linenumber: element.dataset.linenumber,
|
|
55
|
+
filename: element.dataset.filename,
|
|
56
|
+
position: {
|
|
57
|
+
top: rect.top,
|
|
58
|
+
left: rect.left,
|
|
59
|
+
right: rect.right,
|
|
60
|
+
bottom: rect.bottom,
|
|
61
|
+
width: rect.width,
|
|
62
|
+
height: rect.height,
|
|
63
|
+
centerX: rect.left + rect.width / 2,
|
|
64
|
+
centerY: rect.top + rect.height / 2,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
originalContent,
|
|
68
|
+
newContent,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (isStaticArrayTextElement(element)) {
|
|
72
|
+
message.arrIndex = element.dataset.arrIndex;
|
|
73
|
+
message.arrVariableName = element.dataset.arrVariableName;
|
|
74
|
+
message.arrField = element.dataset.arrField;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
window.parent.postMessage(message, "*");
|
|
78
|
+
|
|
79
|
+
element.dataset.originalTextContent = newContent || "";
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const debouncedReport = (element: HTMLElement) => {
|
|
83
|
+
if (debouncedSendTimeout) clearTimeout(debouncedSendTimeout);
|
|
84
|
+
debouncedSendTimeout = setTimeout(() => reportEdit(element), DEBOUNCE_MS);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const onTextInput = (element: HTMLElement) => {
|
|
88
|
+
repositionOverlays();
|
|
89
|
+
debouncedReport(element);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleInputEvent = function (this: HTMLElement) {
|
|
93
|
+
onTextInput(this);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const makeEditable = (element: HTMLElement) => {
|
|
97
|
+
injectFocusOutlineCSS();
|
|
98
|
+
|
|
99
|
+
element.dataset.originalTextContent = element.textContent || "";
|
|
100
|
+
element.dataset.originalCursor = element.style.cursor;
|
|
101
|
+
element.contentEditable = "true";
|
|
102
|
+
element.setAttribute(PLUGIN_ELEMENT_ATTR, "true");
|
|
103
|
+
|
|
104
|
+
const abortController = new AbortController();
|
|
105
|
+
listenerAbortControllers.set(element, abortController);
|
|
106
|
+
element.addEventListener("input", handleInputEvent, {
|
|
107
|
+
signal: abortController.signal,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
element.style.cursor = "text";
|
|
111
|
+
selectText(element);
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
if (element.isConnected) {
|
|
114
|
+
element.focus();
|
|
115
|
+
}
|
|
116
|
+
}, 0);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const makeNonEditable = (element: HTMLElement) => {
|
|
120
|
+
const abortController = listenerAbortControllers.get(element);
|
|
121
|
+
if (abortController) {
|
|
122
|
+
abortController.abort();
|
|
123
|
+
listenerAbortControllers.delete(element);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!element.isConnected) return;
|
|
127
|
+
|
|
128
|
+
removeFocusOutlineCSS();
|
|
129
|
+
element.contentEditable = "false";
|
|
130
|
+
element.removeAttribute(PLUGIN_ELEMENT_ATTR);
|
|
131
|
+
delete element.dataset.originalTextContent;
|
|
132
|
+
|
|
133
|
+
if (element.dataset.originalCursor !== undefined) {
|
|
134
|
+
element.style.cursor = element.dataset.originalCursor;
|
|
135
|
+
delete element.dataset.originalCursor;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// --- Public API ---
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
get enabled() {
|
|
143
|
+
return enabled;
|
|
144
|
+
},
|
|
145
|
+
set enabled(value: boolean) {
|
|
146
|
+
enabled = value;
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
isEditing() {
|
|
150
|
+
return currentEditingElement !== null;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
getCurrentElement() {
|
|
154
|
+
return currentEditingElement;
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
canEdit(element: Element) {
|
|
158
|
+
return shouldEnterInlineEditingMode(element);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
startEditing(element: HTMLElement) {
|
|
162
|
+
currentEditingElement = element;
|
|
163
|
+
|
|
164
|
+
host.getSelectedOverlays().forEach((o) => {
|
|
165
|
+
o.style.display = "none";
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
makeEditable(element);
|
|
169
|
+
|
|
170
|
+
window.parent.postMessage(
|
|
171
|
+
{
|
|
172
|
+
type: "content-editing-started",
|
|
173
|
+
visualSelectorId: host.getSelectedElementId(),
|
|
174
|
+
},
|
|
175
|
+
"*"
|
|
176
|
+
);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
stopEditing() {
|
|
180
|
+
if (!currentEditingElement) return;
|
|
181
|
+
|
|
182
|
+
if (debouncedSendTimeout) {
|
|
183
|
+
clearTimeout(debouncedSendTimeout);
|
|
184
|
+
debouncedSendTimeout = null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const element = currentEditingElement;
|
|
188
|
+
makeNonEditable(element);
|
|
189
|
+
|
|
190
|
+
host.getSelectedOverlays().forEach((o) => {
|
|
191
|
+
o.style.display = "";
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
repositionOverlays();
|
|
195
|
+
|
|
196
|
+
window.parent.postMessage(
|
|
197
|
+
{
|
|
198
|
+
type: "content-editing-ended",
|
|
199
|
+
visualSelectorId: host.getSelectedElementId(),
|
|
200
|
+
},
|
|
201
|
+
"*"
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
currentEditingElement = null;
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
markElementsSelected(elements: Element[]) {
|
|
208
|
+
elements.forEach((el) => {
|
|
209
|
+
if (el instanceof HTMLElement) {
|
|
210
|
+
el.dataset.selected = "true";
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
clearSelectedMarks(elementId: string | null) {
|
|
216
|
+
if (!elementId) return;
|
|
217
|
+
host.findElementsById(elementId).forEach((el) => {
|
|
218
|
+
if (el instanceof HTMLElement) {
|
|
219
|
+
delete el.dataset.selected;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
handleToggleMessage(data: { dataSourceLocation: string; inlineEditingMode: boolean }) {
|
|
225
|
+
if (!enabled) return;
|
|
226
|
+
|
|
227
|
+
const elements = host.findElementsById(data.dataSourceLocation);
|
|
228
|
+
if (elements.length === 0 || !(elements[0] instanceof HTMLElement)) return;
|
|
229
|
+
|
|
230
|
+
const element = elements[0];
|
|
231
|
+
|
|
232
|
+
if (data.inlineEditingMode) {
|
|
233
|
+
if (!shouldEnterInlineEditingMode(element)) return;
|
|
234
|
+
|
|
235
|
+
// Select the element first if not already selected
|
|
236
|
+
if (host.getSelectedElementId() !== data.dataSourceLocation) {
|
|
237
|
+
this.stopEditing();
|
|
238
|
+
host.clearSelection();
|
|
239
|
+
this.markElementsSelected(elements);
|
|
240
|
+
host.createSelectionOverlays(elements, data.dataSourceLocation);
|
|
241
|
+
}
|
|
242
|
+
this.startEditing(element);
|
|
243
|
+
} else {
|
|
244
|
+
if (currentEditingElement === element) {
|
|
245
|
+
this.stopEditing();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
cleanup() {
|
|
251
|
+
this.stopEditing();
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const FOCUS_STYLE_ID = "visual-edit-focus-styles";
|
|
2
|
+
|
|
3
|
+
const EDITABLE_TAGS = [
|
|
4
|
+
"div", "p", "h1", "h2", "h3", "h4", "h5", "h6",
|
|
5
|
+
"span", "li", "td", "a", "button", "label",
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export const isStaticArrayTextElement = (element: HTMLElement): boolean => {
|
|
9
|
+
return !!element.dataset.arrField;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const passesStructuralChecks = (element: HTMLElement): boolean => {
|
|
13
|
+
if (!EDITABLE_TAGS.includes(element.tagName.toLowerCase())) return false;
|
|
14
|
+
if (!element.textContent?.trim()) return false;
|
|
15
|
+
if (element.querySelector("img, video, canvas, svg")) return false;
|
|
16
|
+
if (element.children?.length > 0) return false;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const injectFocusOutlineCSS = () => {
|
|
21
|
+
if (document.getElementById(FOCUS_STYLE_ID)) return;
|
|
22
|
+
|
|
23
|
+
const style = document.createElement("style");
|
|
24
|
+
style.id = FOCUS_STYLE_ID;
|
|
25
|
+
style.textContent = `
|
|
26
|
+
[data-selected="true"][contenteditable="true"]:focus {
|
|
27
|
+
outline: none !important;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
document.head.appendChild(style);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const removeFocusOutlineCSS = () => {
|
|
34
|
+
document.getElementById(FOCUS_STYLE_ID)?.remove();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const selectText = (element: HTMLElement) => {
|
|
38
|
+
const range = document.createRange();
|
|
39
|
+
range.selectNodeContents(element);
|
|
40
|
+
const selection = window.getSelection();
|
|
41
|
+
selection?.removeAllRanges();
|
|
42
|
+
selection?.addRange(range);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const isEditableTextElement = (element: Element): boolean => {
|
|
46
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
47
|
+
if (!passesStructuralChecks(element)) return false;
|
|
48
|
+
if (isStaticArrayTextElement(element)) return true;
|
|
49
|
+
if (element.dataset.dynamicContent === "true") return false;
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const shouldEnterInlineEditingMode = (element: Element): boolean => {
|
|
54
|
+
if (!(element instanceof HTMLElement) || element.dataset.selected !== "true") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return isEditableTextElement(element);
|
|
58
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface CollectionInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
references: string[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface InlineEditHost {
|
|
7
|
+
findElementsById(id: string | null): Element[];
|
|
8
|
+
getSelectedElementId(): string | null;
|
|
9
|
+
getSelectedOverlays(): HTMLDivElement[];
|
|
10
|
+
positionOverlay(
|
|
11
|
+
overlay: HTMLDivElement,
|
|
12
|
+
element: Element,
|
|
13
|
+
isSelected?: boolean
|
|
14
|
+
): void;
|
|
15
|
+
clearSelection(): void;
|
|
16
|
+
createSelectionOverlays(elements: Element[], elementId: string): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ToggleInlineEditData {
|
|
20
|
+
dataSourceLocation: string;
|
|
21
|
+
inlineEditingMode: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface InlineEditController {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
isEditing(): boolean;
|
|
27
|
+
getCurrentElement(): HTMLElement | null;
|
|
28
|
+
canEdit(element: Element): boolean;
|
|
29
|
+
startEditing(element: HTMLElement): void;
|
|
30
|
+
stopEditing(): void;
|
|
31
|
+
markElementsSelected(elements: Element[]): void;
|
|
32
|
+
clearSelectedMarks(elementId: string | null): void;
|
|
33
|
+
handleToggleMessage(data: ToggleInlineEditData): void;
|
|
34
|
+
cleanup(): void;
|
|
35
|
+
}
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const DATA_COLLECTION_ID = "data-collection-id";
|
|
2
|
+
export const DATA_COLLECTION_ITEM_ID = "data-collection-item-id";
|
|
3
|
+
export const DATA_COLLECTION_ITEM_FIELD = "data-collection-item-field";
|
|
4
|
+
export const DATA_COLLECTION_REFERENCE = "data-collection-reference";
|
|
5
|
+
export const DATA_ARR_INDEX = "data-arr-index";
|
|
6
|
+
export const DATA_ARR_VARIABLE_NAME = "data-arr-variable-name";
|
|
7
|
+
export const DATA_ARR_FIELD = "data-arr-field";
|
|
8
|
+
|
|
9
|
+
export const ALLOWED_CUSTOM_COMPONENTS = ["Image", "Link"];
|
|
10
|
+
export const MAX_JSX_DEPTH = 10;
|
|
11
|
+
export const EXCLUDED_FIELDS = ["children", "length"];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { errorOverlayCode } from "./ErrorOverlay.js";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
|
|
4
|
+
export function errorOverlayPlugin() {
|
|
5
|
+
return {
|
|
6
|
+
name: "error-overlay",
|
|
7
|
+
apply: (config) => config.mode === "development",
|
|
8
|
+
transform(code, id, opts = {}) {
|
|
9
|
+
if (opts?.ssr) return;
|
|
10
|
+
|
|
11
|
+
if (!id.includes("vite/dist/client/client.mjs")) return;
|
|
12
|
+
|
|
13
|
+
return code.replace(
|
|
14
|
+
"class ErrorOverlay",
|
|
15
|
+
errorOverlayCode + "\nclass OldErrorOverlay"
|
|
16
|
+
);
|
|
17
|
+
},
|
|
18
|
+
} as Plugin;
|
|
19
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { Plugin, HtmlTagDescriptor } from "vite";
|
|
2
|
+
import { loadEnv } from "vite";
|
|
3
|
+
|
|
4
|
+
type Injection = {
|
|
5
|
+
/** Script src path for dev mode (served from node_modules) */
|
|
6
|
+
src?: string;
|
|
7
|
+
injectTo: "head" | "body";
|
|
8
|
+
mode: "dev" | "production";
|
|
9
|
+
/** Inline content for production builds (src paths don't work in built output) */
|
|
10
|
+
inlineContent?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ANALYTICS_TRACKER_SCRIPT = `
|
|
14
|
+
if (window.self === window.top) {
|
|
15
|
+
let lastPath = "";
|
|
16
|
+
function getPageNameFromPath(path) {
|
|
17
|
+
const segments = path.split("/").filter(Boolean);
|
|
18
|
+
return segments[0] || null;
|
|
19
|
+
}
|
|
20
|
+
function trackPageView() {
|
|
21
|
+
const path = window.location.pathname;
|
|
22
|
+
if (path === lastPath) return;
|
|
23
|
+
lastPath = path;
|
|
24
|
+
const pageName = getPageNameFromPath(path) || "home";
|
|
25
|
+
const appId = import.meta.env.VITE_BASE44_APP_ID;
|
|
26
|
+
if (!appId) return;
|
|
27
|
+
fetch(\`/app-logs/\${appId}/log-user-in-app/\${pageName}\`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
}).catch(() => {});
|
|
30
|
+
}
|
|
31
|
+
const originalPushState = history.pushState.bind(history);
|
|
32
|
+
history.pushState = function (...args) {
|
|
33
|
+
originalPushState(...args);
|
|
34
|
+
trackPageView();
|
|
35
|
+
};
|
|
36
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
37
|
+
history.replaceState = function (...args) {
|
|
38
|
+
originalReplaceState(...args);
|
|
39
|
+
trackPageView();
|
|
40
|
+
};
|
|
41
|
+
window.addEventListener("popstate", trackPageView);
|
|
42
|
+
trackPageView();
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const INJECTIONS: Record<string, Injection> = {
|
|
47
|
+
unhandledErrors: {
|
|
48
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/unhandled-errors-handlers.js",
|
|
49
|
+
injectTo: "head",
|
|
50
|
+
mode: "dev",
|
|
51
|
+
},
|
|
52
|
+
sandboxMount: {
|
|
53
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/sandbox-mount-observer.js",
|
|
54
|
+
injectTo: "body",
|
|
55
|
+
mode: "dev",
|
|
56
|
+
},
|
|
57
|
+
hmrNotifier: {
|
|
58
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/sandbox-hmr-notifier.js",
|
|
59
|
+
injectTo: "head",
|
|
60
|
+
mode: "dev",
|
|
61
|
+
},
|
|
62
|
+
navigationNotifier: {
|
|
63
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/navigation-notifier.js",
|
|
64
|
+
injectTo: "head",
|
|
65
|
+
mode: "dev",
|
|
66
|
+
},
|
|
67
|
+
analyticsTracker: {
|
|
68
|
+
injectTo: "head",
|
|
69
|
+
mode: "production",
|
|
70
|
+
inlineContent: ANALYTICS_TRACKER_SCRIPT,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export function htmlInjectionsPlugin({
|
|
75
|
+
hmrNotifier,
|
|
76
|
+
navigationNotifier,
|
|
77
|
+
visualEditAgent,
|
|
78
|
+
analyticsTracker,
|
|
79
|
+
}: {
|
|
80
|
+
hmrNotifier: boolean;
|
|
81
|
+
navigationNotifier: boolean;
|
|
82
|
+
visualEditAgent: boolean;
|
|
83
|
+
analyticsTracker: boolean;
|
|
84
|
+
}) {
|
|
85
|
+
const enabledInjections: Record<string, boolean> = {
|
|
86
|
+
unhandledErrors: true,
|
|
87
|
+
sandboxMount: true,
|
|
88
|
+
hmrNotifier,
|
|
89
|
+
navigationNotifier,
|
|
90
|
+
analyticsTracker,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
let resolvedEnv: Record<string, string> = {};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
name: "html-injections",
|
|
97
|
+
configResolved(config) {
|
|
98
|
+
// Capture env vars for use in transformIndexHtml
|
|
99
|
+
// loadEnv gets all env vars including VITE_ prefixed ones
|
|
100
|
+
resolvedEnv = loadEnv(config.mode, config.root, "");
|
|
101
|
+
},
|
|
102
|
+
transformIndexHtml: {
|
|
103
|
+
handler(_html, ctx) {
|
|
104
|
+
const currentMode = ctx.server ? "dev" : "production";
|
|
105
|
+
|
|
106
|
+
const tags = Object.entries(INJECTIONS)
|
|
107
|
+
.filter(
|
|
108
|
+
([key, injection]) =>
|
|
109
|
+
enabledInjections[key] && injection.mode === currentMode
|
|
110
|
+
)
|
|
111
|
+
.map(([, injection]): HtmlTagDescriptor => {
|
|
112
|
+
// Production injections use inline content (src paths don't work in built output)
|
|
113
|
+
if (injection.inlineContent) {
|
|
114
|
+
// Replace import.meta.env references with actual values
|
|
115
|
+
// Vite doesn't replace these in inline script content
|
|
116
|
+
let content = injection.inlineContent;
|
|
117
|
+
content = content.replace(
|
|
118
|
+
/import\.meta\.env\.(\w+)/g,
|
|
119
|
+
(_, envKey) => JSON.stringify(resolvedEnv[envKey] ?? "")
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
tag: "script",
|
|
124
|
+
attrs: { type: "module" },
|
|
125
|
+
children: content,
|
|
126
|
+
injectTo: injection.injectTo,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Dev injections use src path (served from node_modules)
|
|
130
|
+
return {
|
|
131
|
+
tag: "script",
|
|
132
|
+
attrs: {
|
|
133
|
+
src: injection.src,
|
|
134
|
+
type: "module",
|
|
135
|
+
},
|
|
136
|
+
injectTo: injection.injectTo,
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Visual edit agent — loaded via dynamic import to support
|
|
141
|
+
// local dev iteration with ?sandbox-bridge=local
|
|
142
|
+
if (currentMode === "dev" && visualEditAgent) {
|
|
143
|
+
const dist = "/node_modules/@base44/vite-plugin/dist";
|
|
144
|
+
tags.push({
|
|
145
|
+
tag: "script",
|
|
146
|
+
attrs: { type: "module" },
|
|
147
|
+
children: [
|
|
148
|
+
`if (window.self !== window.top) {`,
|
|
149
|
+
` const mode = new URLSearchParams(location.search).get("sandbox-bridge");`,
|
|
150
|
+
` const url = mode === "local"`,
|
|
151
|
+
` ? "https://localhost:3201/index.mjs"`,
|
|
152
|
+
` : "${dist}/statics/index.mjs";`,
|
|
153
|
+
` import(url)`,
|
|
154
|
+
` .then(mod => { if (typeof mod.setupVisualEditAgent === "function") mod.setupVisualEditAgent(); })`,
|
|
155
|
+
` .catch(e => console.error("[visual-edit-agent] Failed to load:", e));`,
|
|
156
|
+
`}`,
|
|
157
|
+
].join("\n"),
|
|
158
|
+
injectTo: "body",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return tags;
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
} as Plugin;
|
|
166
|
+
}
|