@yoamigo.com/core 0.1.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/LICENSE +11 -0
- package/README.md +81 -0
- package/dist/MarkdownText-mylt-QX-.d.ts +106 -0
- package/dist/api-client-D8FkeBAI.d.ts +40 -0
- package/dist/asset-resolver-BnIvDkVv.d.ts +72 -0
- package/dist/builder-selection-CYP91nRu.d.ts +6 -0
- package/dist/index.css +372 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.js +2579 -0
- package/dist/lib-prod.d.ts +13 -0
- package/dist/lib-prod.js +108 -0
- package/dist/lib.d.ts +84 -0
- package/dist/lib.js +736 -0
- package/dist/plugin.d.ts +47 -0
- package/dist/plugin.js +96 -0
- package/dist/prod.d.ts +31 -0
- package/dist/prod.js +295 -0
- package/dist/router.d.ts +46 -0
- package/dist/router.js +42 -0
- package/package.json +115 -0
- package/src/styles/index.css +5 -0
package/dist/lib.js
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
// src/lib/content-registry.ts
|
|
2
|
+
var contentMap = /* @__PURE__ */ new Map();
|
|
3
|
+
function registerContent(content) {
|
|
4
|
+
contentMap = new Map(Object.entries(content));
|
|
5
|
+
}
|
|
6
|
+
function getContent(fieldId) {
|
|
7
|
+
return contentMap.get(fieldId) ?? "";
|
|
8
|
+
}
|
|
9
|
+
function getAllContent() {
|
|
10
|
+
return Object.fromEntries(contentMap);
|
|
11
|
+
}
|
|
12
|
+
function hasContent(fieldId) {
|
|
13
|
+
return contentMap.has(fieldId);
|
|
14
|
+
}
|
|
15
|
+
var contentRegistry = {
|
|
16
|
+
registerContent,
|
|
17
|
+
getContent,
|
|
18
|
+
getAllContent,
|
|
19
|
+
hasContent
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/lib/asset-resolver.ts
|
|
23
|
+
var assetResolver = (path) => path;
|
|
24
|
+
function setAssetResolver(resolver) {
|
|
25
|
+
assetResolver = resolver;
|
|
26
|
+
}
|
|
27
|
+
function resolveAssetUrl(path) {
|
|
28
|
+
if (!path) return "";
|
|
29
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
30
|
+
return path;
|
|
31
|
+
}
|
|
32
|
+
return assetResolver(path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/lib/builder-selection.ts
|
|
36
|
+
var SELECTABLE_SELECTORS = [
|
|
37
|
+
// Interactive elements
|
|
38
|
+
"button",
|
|
39
|
+
// Buttons
|
|
40
|
+
"a[href]",
|
|
41
|
+
// Links with href
|
|
42
|
+
'[role="button"]',
|
|
43
|
+
// ARIA buttons
|
|
44
|
+
// Form elements
|
|
45
|
+
"input",
|
|
46
|
+
// Input fields (text, checkbox, etc.)
|
|
47
|
+
"textarea",
|
|
48
|
+
// Text areas
|
|
49
|
+
"select",
|
|
50
|
+
// Dropdown selects
|
|
51
|
+
"label",
|
|
52
|
+
// Form labels
|
|
53
|
+
"form",
|
|
54
|
+
// Forms as containers
|
|
55
|
+
// Media elements
|
|
56
|
+
"img",
|
|
57
|
+
// Images
|
|
58
|
+
"video",
|
|
59
|
+
// Videos
|
|
60
|
+
"audio",
|
|
61
|
+
// Audio players
|
|
62
|
+
"iframe",
|
|
63
|
+
// Embedded content
|
|
64
|
+
"svg",
|
|
65
|
+
// SVG graphics
|
|
66
|
+
"figure",
|
|
67
|
+
// Figures with captions
|
|
68
|
+
"figcaption",
|
|
69
|
+
// Figure captions
|
|
70
|
+
// Text/content elements
|
|
71
|
+
"h1",
|
|
72
|
+
"h2",
|
|
73
|
+
"h3",
|
|
74
|
+
"h4",
|
|
75
|
+
"h5",
|
|
76
|
+
"h6",
|
|
77
|
+
// Headings
|
|
78
|
+
"p",
|
|
79
|
+
// Paragraphs
|
|
80
|
+
"blockquote",
|
|
81
|
+
// Block quotes
|
|
82
|
+
"table",
|
|
83
|
+
// Tables
|
|
84
|
+
"tr",
|
|
85
|
+
// Table rows
|
|
86
|
+
"td",
|
|
87
|
+
// Table cells
|
|
88
|
+
"th",
|
|
89
|
+
// Table headers
|
|
90
|
+
// List elements
|
|
91
|
+
"li",
|
|
92
|
+
// List items (event cards, etc.)
|
|
93
|
+
"ol",
|
|
94
|
+
// Ordered lists
|
|
95
|
+
"ul",
|
|
96
|
+
// Unordered lists
|
|
97
|
+
// Other
|
|
98
|
+
"article",
|
|
99
|
+
// Article cards
|
|
100
|
+
"[data-mp-selectable]"
|
|
101
|
+
// Explicit selectables (backward compatible)
|
|
102
|
+
];
|
|
103
|
+
var CONTAINER_SELECTORS = [
|
|
104
|
+
"section",
|
|
105
|
+
"[data-mp-editable]",
|
|
106
|
+
"nav",
|
|
107
|
+
"header",
|
|
108
|
+
"footer"
|
|
109
|
+
];
|
|
110
|
+
var BuilderSelectionManager = class {
|
|
111
|
+
enabled = false;
|
|
112
|
+
selections = /* @__PURE__ */ new Map();
|
|
113
|
+
hoverOverlay = null;
|
|
114
|
+
currentHoveredElement = null;
|
|
115
|
+
// Cache element references for lookup when syncing selections
|
|
116
|
+
elementMap = /* @__PURE__ */ new Map();
|
|
117
|
+
// Current selections from parent (for re-rendering on mode change)
|
|
118
|
+
currentSelections = [];
|
|
119
|
+
constructor() {
|
|
120
|
+
if (window.parent === window) {
|
|
121
|
+
console.log("[BuilderSelection] Not in iframe, skipping initialization");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log("[BuilderSelection] Initializing in iframe context");
|
|
125
|
+
this.setupMessageListener();
|
|
126
|
+
this.createHoverOverlay();
|
|
127
|
+
this.setupScrollResizeListeners();
|
|
128
|
+
this.setupKeyboardListener();
|
|
129
|
+
this.setupWheelForwarding();
|
|
130
|
+
this.notifyPageReady();
|
|
131
|
+
window.addEventListener("popstate", () => this.notifyPageReady());
|
|
132
|
+
}
|
|
133
|
+
notifyPageReady() {
|
|
134
|
+
this.sendToParent({
|
|
135
|
+
type: "PAGE_READY",
|
|
136
|
+
page: window.location.pathname
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
sendToParent(message) {
|
|
140
|
+
try {
|
|
141
|
+
window.parent.postMessage(message, "*");
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("[BuilderSelection] Failed to send message to parent:", error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
setupMessageListener() {
|
|
147
|
+
window.addEventListener("message", (event) => {
|
|
148
|
+
const data = event.data;
|
|
149
|
+
if (!data?.type) return;
|
|
150
|
+
switch (data.type) {
|
|
151
|
+
case "SELECTOR_MODE":
|
|
152
|
+
this.setEnabled(data.enabled ?? false);
|
|
153
|
+
break;
|
|
154
|
+
case "SELECTION_SYNC":
|
|
155
|
+
this.syncSelections(data.selections ?? [], data.currentPage ?? "");
|
|
156
|
+
break;
|
|
157
|
+
case "CLEAR_SELECTIONS":
|
|
158
|
+
this.clearAllSelections();
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
setEnabled(enabled) {
|
|
164
|
+
console.log("[BuilderSelection] Selector mode:", enabled);
|
|
165
|
+
this.enabled = enabled;
|
|
166
|
+
window.__builderSelectModeEnabled = enabled;
|
|
167
|
+
if (enabled) {
|
|
168
|
+
document.body.style.cursor = "crosshair";
|
|
169
|
+
document.body.classList.add("builder-selector-active");
|
|
170
|
+
this.attachEventListeners();
|
|
171
|
+
this.renderSelectionMasks();
|
|
172
|
+
} else {
|
|
173
|
+
document.body.style.cursor = "";
|
|
174
|
+
document.body.classList.remove("builder-selector-active");
|
|
175
|
+
this.detachEventListeners();
|
|
176
|
+
this.hideHoverOverlay();
|
|
177
|
+
this.clearAllSelections();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
attachEventListeners() {
|
|
181
|
+
document.addEventListener("mouseover", this.handleMouseOver);
|
|
182
|
+
document.addEventListener("mouseout", this.handleMouseOut);
|
|
183
|
+
document.addEventListener("click", this.handleClick, true);
|
|
184
|
+
}
|
|
185
|
+
detachEventListeners() {
|
|
186
|
+
document.removeEventListener("mouseover", this.handleMouseOver);
|
|
187
|
+
document.removeEventListener("mouseout", this.handleMouseOut);
|
|
188
|
+
document.removeEventListener("click", this.handleClick, true);
|
|
189
|
+
}
|
|
190
|
+
handleMouseOver = (e) => {
|
|
191
|
+
if (!this.enabled) return;
|
|
192
|
+
const target = this.findDeepestSelectableAt(e.target);
|
|
193
|
+
if (!target || target === this.currentHoveredElement) return;
|
|
194
|
+
this.currentHoveredElement = target;
|
|
195
|
+
this.showHoverOverlay(target);
|
|
196
|
+
const selectorId = this.generateSelectorId(target);
|
|
197
|
+
this.sendToParent({
|
|
198
|
+
type: "ELEMENT_HOVER",
|
|
199
|
+
selectorId
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
handleMouseOut = (e) => {
|
|
203
|
+
if (!this.enabled) return;
|
|
204
|
+
const relatedTarget = e.relatedTarget;
|
|
205
|
+
const stillInSelectable = relatedTarget ? this.findDeepestSelectableAt(relatedTarget) : null;
|
|
206
|
+
if (!stillInSelectable || stillInSelectable !== this.currentHoveredElement) {
|
|
207
|
+
this.currentHoveredElement = null;
|
|
208
|
+
this.hideHoverOverlay();
|
|
209
|
+
this.sendToParent({
|
|
210
|
+
type: "ELEMENT_HOVER",
|
|
211
|
+
selectorId: null
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
handleClick = (e) => {
|
|
216
|
+
if (!this.enabled) return;
|
|
217
|
+
const target = this.findDeepestSelectableAt(e.target);
|
|
218
|
+
if (!target) return;
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
e.stopPropagation();
|
|
221
|
+
const selectorId = this.generateSelectorId(target);
|
|
222
|
+
const label = this.deriveLabel(target);
|
|
223
|
+
this.elementMap.set(selectorId, target);
|
|
224
|
+
this.sendToParent({
|
|
225
|
+
type: "ELEMENT_CLICKED",
|
|
226
|
+
selectorId,
|
|
227
|
+
label,
|
|
228
|
+
page: window.location.pathname,
|
|
229
|
+
modifier: e.shiftKey ? "shift" : e.metaKey || e.ctrlKey ? "cmd" : null
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
handleKeyDown = (e) => {
|
|
233
|
+
if (e.key === "Shift") {
|
|
234
|
+
const activeElement = document.activeElement;
|
|
235
|
+
const isEditing = activeElement?.closest(".ya-text-editing") || activeElement?.closest(".ya-link-editing");
|
|
236
|
+
if (!isEditing) {
|
|
237
|
+
this.sendToParent({ type: "SHIFT_KEY_PRESSED" });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Check if element matches any selectable selector
|
|
243
|
+
*/
|
|
244
|
+
isSelectableElement(el) {
|
|
245
|
+
for (const selector of SELECTABLE_SELECTORS) {
|
|
246
|
+
if (el.matches(selector)) return true;
|
|
247
|
+
}
|
|
248
|
+
for (const selector of CONTAINER_SELECTORS) {
|
|
249
|
+
if (el.matches(selector)) return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Find the deepest selectable element from the click/hover target
|
|
255
|
+
* Walks up the DOM tree and returns the first (deepest) selectable found
|
|
256
|
+
*/
|
|
257
|
+
findDeepestSelectableAt(target) {
|
|
258
|
+
if (target.closest(".mp-text-editing")) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
let current = target;
|
|
262
|
+
const selectables = [];
|
|
263
|
+
while (current && current !== document.body) {
|
|
264
|
+
if (this.isSelectableElement(current)) {
|
|
265
|
+
selectables.push(current);
|
|
266
|
+
}
|
|
267
|
+
current = current.parentElement;
|
|
268
|
+
}
|
|
269
|
+
return selectables[0] || null;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Generate a unique selector ID for an element
|
|
273
|
+
* Uses data-mp-selectable if available, otherwise generates from context
|
|
274
|
+
*/
|
|
275
|
+
generateSelectorId(el) {
|
|
276
|
+
if (el.dataset.mpSelectable) return el.dataset.mpSelectable;
|
|
277
|
+
if (el.id) return el.id;
|
|
278
|
+
const tag = el.tagName.toLowerCase();
|
|
279
|
+
const parent = el.closest("section, [data-mp-selectable], [data-mp-editable]");
|
|
280
|
+
const parentId = parent?.dataset?.mpSelectable || parent?.dataset?.mpEditable || parent?.id || "page";
|
|
281
|
+
const siblings = parent ? Array.from(parent.querySelectorAll(tag)) : [];
|
|
282
|
+
const index = siblings.indexOf(el);
|
|
283
|
+
return `${parentId}.${tag}${index > 0 ? `.${index}` : ""}`;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Derive a human-readable label from the element
|
|
287
|
+
*/
|
|
288
|
+
deriveLabel(el) {
|
|
289
|
+
if (el.dataset.mpSelectable) {
|
|
290
|
+
return this.toTitleCase(el.dataset.mpSelectable);
|
|
291
|
+
}
|
|
292
|
+
const tag = el.tagName.toLowerCase();
|
|
293
|
+
const text = el.textContent?.trim().slice(0, 30) || "";
|
|
294
|
+
if (tag === "button" || el.getAttribute("role") === "button") {
|
|
295
|
+
return text ? `"${text}" Button` : "Button";
|
|
296
|
+
}
|
|
297
|
+
if (tag === "a") {
|
|
298
|
+
return text ? `"${text}" Link` : "Link";
|
|
299
|
+
}
|
|
300
|
+
if (tag === "img") {
|
|
301
|
+
return el.alt || "Image";
|
|
302
|
+
}
|
|
303
|
+
if (tag === "video") {
|
|
304
|
+
return "Video";
|
|
305
|
+
}
|
|
306
|
+
if (tag === "audio") {
|
|
307
|
+
return "Audio Player";
|
|
308
|
+
}
|
|
309
|
+
if (tag === "iframe") {
|
|
310
|
+
return "Embedded Content";
|
|
311
|
+
}
|
|
312
|
+
if (tag === "svg") {
|
|
313
|
+
return "SVG Graphic";
|
|
314
|
+
}
|
|
315
|
+
if (tag === "figure") {
|
|
316
|
+
const caption = el.querySelector("figcaption")?.textContent?.trim();
|
|
317
|
+
return caption ? `Figure: ${caption.slice(0, 20)}` : "Figure";
|
|
318
|
+
}
|
|
319
|
+
if (tag === "figcaption") {
|
|
320
|
+
return text ? `Caption: ${text.slice(0, 20)}` : "Caption";
|
|
321
|
+
}
|
|
322
|
+
if (tag === "input") {
|
|
323
|
+
const type = el.type || "text";
|
|
324
|
+
const placeholder = el.placeholder;
|
|
325
|
+
if (type === "submit") return "Submit Button";
|
|
326
|
+
if (type === "checkbox") return "Checkbox";
|
|
327
|
+
if (type === "radio") return "Radio Button";
|
|
328
|
+
return placeholder ? `"${placeholder}" Input` : `${type.charAt(0).toUpperCase() + type.slice(1)} Input`;
|
|
329
|
+
}
|
|
330
|
+
if (tag === "textarea") {
|
|
331
|
+
const placeholder = el.placeholder;
|
|
332
|
+
return placeholder ? `"${placeholder}" Text Area` : "Text Area";
|
|
333
|
+
}
|
|
334
|
+
if (tag === "select") {
|
|
335
|
+
return "Dropdown";
|
|
336
|
+
}
|
|
337
|
+
if (tag === "label") {
|
|
338
|
+
return text ? `Label: ${text.slice(0, 20)}` : "Label";
|
|
339
|
+
}
|
|
340
|
+
if (tag === "form") {
|
|
341
|
+
return "Form";
|
|
342
|
+
}
|
|
343
|
+
if (["h1", "h2", "h3", "h4", "h5", "h6"].includes(tag)) {
|
|
344
|
+
return text ? `Heading: ${text.slice(0, 25)}` : `${tag.toUpperCase()} Heading`;
|
|
345
|
+
}
|
|
346
|
+
if (tag === "p") {
|
|
347
|
+
return text ? `Paragraph: ${text.slice(0, 20)}...` : "Paragraph";
|
|
348
|
+
}
|
|
349
|
+
if (tag === "blockquote") {
|
|
350
|
+
return text ? `Quote: ${text.slice(0, 20)}...` : "Block Quote";
|
|
351
|
+
}
|
|
352
|
+
if (tag === "table") {
|
|
353
|
+
return "Table";
|
|
354
|
+
}
|
|
355
|
+
if (tag === "tr") {
|
|
356
|
+
return "Table Row";
|
|
357
|
+
}
|
|
358
|
+
if (tag === "td" || tag === "th") {
|
|
359
|
+
return text ? `Cell: ${text.slice(0, 15)}` : "Table Cell";
|
|
360
|
+
}
|
|
361
|
+
if (tag === "ol") {
|
|
362
|
+
return "Ordered List";
|
|
363
|
+
}
|
|
364
|
+
if (tag === "ul") {
|
|
365
|
+
return "Unordered List";
|
|
366
|
+
}
|
|
367
|
+
if (tag === "li") {
|
|
368
|
+
const preview = text.length > 20 ? text.slice(0, 20) + "..." : text;
|
|
369
|
+
return preview ? `List Item: ${preview}` : "List Item";
|
|
370
|
+
}
|
|
371
|
+
if (tag === "section") {
|
|
372
|
+
const heading = el.querySelector("h1, h2, h3")?.textContent?.trim();
|
|
373
|
+
return heading ? `${heading} Section` : "Section";
|
|
374
|
+
}
|
|
375
|
+
if (tag === "article") {
|
|
376
|
+
const heading = el.querySelector("h1, h2, h3, h4")?.textContent?.trim();
|
|
377
|
+
return heading ? heading : "Article";
|
|
378
|
+
}
|
|
379
|
+
if (tag === "nav") {
|
|
380
|
+
return "Navigation";
|
|
381
|
+
}
|
|
382
|
+
if (tag === "header") {
|
|
383
|
+
return "Header";
|
|
384
|
+
}
|
|
385
|
+
if (tag === "footer") {
|
|
386
|
+
return "Footer";
|
|
387
|
+
}
|
|
388
|
+
return text || this.toTitleCase(tag);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Convert kebab-case or tag names to Title Case
|
|
392
|
+
*/
|
|
393
|
+
toTitleCase(str) {
|
|
394
|
+
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
395
|
+
}
|
|
396
|
+
createHoverOverlay() {
|
|
397
|
+
this.hoverOverlay = document.createElement("div");
|
|
398
|
+
this.hoverOverlay.id = "builder-hover-overlay";
|
|
399
|
+
this.hoverOverlay.style.cssText = `
|
|
400
|
+
position: fixed;
|
|
401
|
+
pointer-events: none;
|
|
402
|
+
border: 2px dashed #3B82F6;
|
|
403
|
+
background: rgba(59, 130, 246, 0.05);
|
|
404
|
+
z-index: 9999;
|
|
405
|
+
display: none;
|
|
406
|
+
transition: all 0.15s ease-out;
|
|
407
|
+
border-radius: 4px;
|
|
408
|
+
`;
|
|
409
|
+
document.body.appendChild(this.hoverOverlay);
|
|
410
|
+
}
|
|
411
|
+
setupScrollResizeListeners() {
|
|
412
|
+
window.addEventListener("scroll", this.updateSelectionPositions, { passive: true });
|
|
413
|
+
window.addEventListener("resize", this.updateSelectionPositions, { passive: true });
|
|
414
|
+
}
|
|
415
|
+
setupKeyboardListener() {
|
|
416
|
+
document.addEventListener("keydown", this.handleKeyDown);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Forward wheel events with ctrlKey (pinch zoom) to parent
|
|
420
|
+
* This allows pinch-to-zoom gestures inside the iframe to control
|
|
421
|
+
* the parent's zoom state instead of zooming the iframe content
|
|
422
|
+
*/
|
|
423
|
+
setupWheelForwarding() {
|
|
424
|
+
document.addEventListener("wheel", (e) => {
|
|
425
|
+
if (e.ctrlKey) {
|
|
426
|
+
e.preventDefault();
|
|
427
|
+
this.sendToParent({
|
|
428
|
+
type: "IFRAME_WHEEL",
|
|
429
|
+
deltaY: e.deltaY,
|
|
430
|
+
clientX: e.clientX,
|
|
431
|
+
clientY: e.clientY,
|
|
432
|
+
ctrlKey: true
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}, { passive: false });
|
|
436
|
+
}
|
|
437
|
+
showHoverOverlay(element) {
|
|
438
|
+
if (!this.hoverOverlay) return;
|
|
439
|
+
const rect = element.getBoundingClientRect();
|
|
440
|
+
this.hoverOverlay.style.top = `${rect.top}px`;
|
|
441
|
+
this.hoverOverlay.style.left = `${rect.left}px`;
|
|
442
|
+
this.hoverOverlay.style.width = `${rect.width}px`;
|
|
443
|
+
this.hoverOverlay.style.height = `${rect.height}px`;
|
|
444
|
+
this.hoverOverlay.style.display = "block";
|
|
445
|
+
}
|
|
446
|
+
hideHoverOverlay() {
|
|
447
|
+
if (this.hoverOverlay) {
|
|
448
|
+
this.hoverOverlay.style.display = "none";
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
syncSelections(selections, _currentPage) {
|
|
452
|
+
console.log("[BuilderSelection] Syncing selections:", selections.length, "current page:", _currentPage);
|
|
453
|
+
this.currentSelections = selections;
|
|
454
|
+
if (!this.enabled) {
|
|
455
|
+
this.clearAllSelections();
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
this.renderSelectionMasks();
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Render selection masks for current selections (only when enabled)
|
|
462
|
+
*/
|
|
463
|
+
renderSelectionMasks() {
|
|
464
|
+
this.clearAllSelections();
|
|
465
|
+
for (const sel of this.currentSelections) {
|
|
466
|
+
if (sel.page !== window.location.pathname) continue;
|
|
467
|
+
const element = this.findElementBySelectorId(sel.selectorId);
|
|
468
|
+
if (!element) {
|
|
469
|
+
console.warn(`[BuilderSelection] Element not found for selector: ${sel.selectorId}`);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
this.renderSelectionIndicator(element, sel.id, sel.color);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Find element by selector ID (supports both explicit and auto-generated IDs)
|
|
477
|
+
*/
|
|
478
|
+
findElementBySelectorId(selectorId) {
|
|
479
|
+
const explicit = document.querySelector(`[data-mp-selectable="${selectorId}"]`);
|
|
480
|
+
if (explicit) return explicit;
|
|
481
|
+
const byId = document.getElementById(selectorId);
|
|
482
|
+
if (byId) return byId;
|
|
483
|
+
const cached = this.elementMap.get(selectorId);
|
|
484
|
+
if (cached && document.body.contains(cached)) return cached;
|
|
485
|
+
const parts = selectorId.split(".");
|
|
486
|
+
if (parts.length >= 2) {
|
|
487
|
+
const parentId = parts[0];
|
|
488
|
+
const tag = parts[1];
|
|
489
|
+
const index = parts.length > 2 ? parseInt(parts[2], 10) : 0;
|
|
490
|
+
let parent = null;
|
|
491
|
+
if (parentId === "page") {
|
|
492
|
+
parent = document.body;
|
|
493
|
+
} else {
|
|
494
|
+
parent = document.querySelector(`[data-mp-selectable="${parentId}"]`) || document.querySelector(`[data-mp-editable="${parentId}"]`) || document.getElementById(parentId) || document.querySelector(`section#${parentId}`);
|
|
495
|
+
}
|
|
496
|
+
if (parent) {
|
|
497
|
+
const candidates = parent.querySelectorAll(tag);
|
|
498
|
+
if (candidates[index]) {
|
|
499
|
+
return candidates[index];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
clearAllSelections() {
|
|
506
|
+
this.selections.forEach(({ container }) => {
|
|
507
|
+
container.remove();
|
|
508
|
+
});
|
|
509
|
+
this.selections.clear();
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Update positions of selection overlays when viewport changes (scroll/resize)
|
|
513
|
+
*/
|
|
514
|
+
updateSelectionPositions = () => {
|
|
515
|
+
this.selections.forEach(({ element, container }) => {
|
|
516
|
+
if (!document.body.contains(element)) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const rect = element.getBoundingClientRect();
|
|
520
|
+
container.style.top = `${rect.top}px`;
|
|
521
|
+
container.style.left = `${rect.left}px`;
|
|
522
|
+
container.style.width = `${rect.width}px`;
|
|
523
|
+
container.style.height = `${rect.height}px`;
|
|
524
|
+
});
|
|
525
|
+
};
|
|
526
|
+
renderSelectionIndicator(element, selectionId, color) {
|
|
527
|
+
const rect = element.getBoundingClientRect();
|
|
528
|
+
const container = document.createElement("div");
|
|
529
|
+
container.className = "builder-selection-container";
|
|
530
|
+
container.dataset.selectionId = selectionId;
|
|
531
|
+
container.style.cssText = `
|
|
532
|
+
position: fixed;
|
|
533
|
+
top: ${rect.top}px;
|
|
534
|
+
left: ${rect.left}px;
|
|
535
|
+
width: ${rect.width}px;
|
|
536
|
+
height: ${rect.height}px;
|
|
537
|
+
pointer-events: none;
|
|
538
|
+
z-index: 9999;
|
|
539
|
+
`;
|
|
540
|
+
const border = document.createElement("div");
|
|
541
|
+
border.className = "builder-selection-border";
|
|
542
|
+
border.style.cssText = `
|
|
543
|
+
position: absolute;
|
|
544
|
+
inset: 0;
|
|
545
|
+
border: 2px solid ${color};
|
|
546
|
+
border-radius: 4px;
|
|
547
|
+
pointer-events: none;
|
|
548
|
+
`;
|
|
549
|
+
container.appendChild(border);
|
|
550
|
+
const badge = document.createElement("div");
|
|
551
|
+
badge.textContent = selectionId;
|
|
552
|
+
badge.className = "builder-selection-badge";
|
|
553
|
+
badge.style.cssText = `
|
|
554
|
+
position: absolute;
|
|
555
|
+
top: 8px;
|
|
556
|
+
left: 8px;
|
|
557
|
+
background: ${color};
|
|
558
|
+
color: white;
|
|
559
|
+
font-size: 11px;
|
|
560
|
+
font-weight: 600;
|
|
561
|
+
padding: 4px 10px;
|
|
562
|
+
border-radius: 9999px;
|
|
563
|
+
pointer-events: none;
|
|
564
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
565
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
566
|
+
`;
|
|
567
|
+
container.appendChild(badge);
|
|
568
|
+
document.body.appendChild(container);
|
|
569
|
+
this.selections.set(selectionId, { element, container, badge, border });
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
function initBuilderSelection() {
|
|
573
|
+
if (typeof window !== "undefined" && window.parent !== window) {
|
|
574
|
+
if (document.readyState === "loading") {
|
|
575
|
+
document.addEventListener("DOMContentLoaded", () => new BuilderSelectionManager());
|
|
576
|
+
} else {
|
|
577
|
+
new BuilderSelectionManager();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/lib/image-cache.ts
|
|
583
|
+
var cache = /* @__PURE__ */ new Map();
|
|
584
|
+
function setCachedImages(images) {
|
|
585
|
+
cache.clear();
|
|
586
|
+
const timestamp = Date.now();
|
|
587
|
+
for (const img of images) {
|
|
588
|
+
cache.set(img.key, {
|
|
589
|
+
blobUrl: img.blobUrl,
|
|
590
|
+
originalUrl: img.originalUrl,
|
|
591
|
+
timestamp
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function getCachedUrl(src) {
|
|
596
|
+
if (!src) return null;
|
|
597
|
+
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
598
|
+
const exact = cache.get(src);
|
|
599
|
+
if (exact) return exact.blobUrl;
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const direct = cache.get(src);
|
|
603
|
+
if (direct) return direct.blobUrl;
|
|
604
|
+
const filename = src.split("/").pop();
|
|
605
|
+
if (filename) {
|
|
606
|
+
const byFilename = cache.get(filename);
|
|
607
|
+
if (byFilename) return byFilename.blobUrl;
|
|
608
|
+
const withPrefix = cache.get(`assets/${filename}`);
|
|
609
|
+
if (withPrefix) return withPrefix.blobUrl;
|
|
610
|
+
for (const [key, entry] of cache) {
|
|
611
|
+
const keyFilename = key.split("/").pop();
|
|
612
|
+
if (keyFilename === filename) {
|
|
613
|
+
return entry.blobUrl;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
function clearImageCache() {
|
|
620
|
+
cache.clear();
|
|
621
|
+
}
|
|
622
|
+
function getCacheStats() {
|
|
623
|
+
return {
|
|
624
|
+
count: cache.size,
|
|
625
|
+
keys: Array.from(cache.keys())
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function hasCachedImage(src) {
|
|
629
|
+
return getCachedUrl(src) !== null;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/lib/image-cache-listener.ts
|
|
633
|
+
var isListening = false;
|
|
634
|
+
function handleMessage(event) {
|
|
635
|
+
const data = event.data;
|
|
636
|
+
if (data?.type === "YA_IMAGE_CACHE_UPDATE") {
|
|
637
|
+
console.log("[ImageCache] Received cache update:", data.images.length, "images");
|
|
638
|
+
setCachedImages(data.images);
|
|
639
|
+
}
|
|
640
|
+
if (data?.type === "YA_IMAGE_CACHE_CLEAR") {
|
|
641
|
+
console.log("[ImageCache] Cache cleared by parent");
|
|
642
|
+
clearImageCache();
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function initImageCacheListener() {
|
|
646
|
+
if (isListening) {
|
|
647
|
+
window.parent.postMessage({ type: "YA_IMAGE_CACHE_REQUEST" }, "*");
|
|
648
|
+
return () => {
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
window.addEventListener("message", handleMessage);
|
|
652
|
+
isListening = true;
|
|
653
|
+
window.parent.postMessage({ type: "YA_IMAGE_CACHE_REQUEST" }, "*");
|
|
654
|
+
return () => {
|
|
655
|
+
window.removeEventListener("message", handleMessage);
|
|
656
|
+
isListening = false;
|
|
657
|
+
clearImageCache();
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/lib/api-client.ts
|
|
662
|
+
function getApiUrl() {
|
|
663
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
664
|
+
const apiUrl = runtimeConfig?.apiUrl || import.meta.env.YA_API_URL;
|
|
665
|
+
if (!apiUrl) {
|
|
666
|
+
throw new Error("API URL not configured (check YOAMIGO_CONFIG or YA_API_URL)");
|
|
667
|
+
}
|
|
668
|
+
return apiUrl;
|
|
669
|
+
}
|
|
670
|
+
function getSiteId() {
|
|
671
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
672
|
+
const siteId = runtimeConfig?.siteId || import.meta.env.YA_SITE_ID;
|
|
673
|
+
if (!siteId) {
|
|
674
|
+
throw new Error("Site ID not configured (check YOAMIGO_CONFIG or YA_SITE_ID)");
|
|
675
|
+
}
|
|
676
|
+
return siteId;
|
|
677
|
+
}
|
|
678
|
+
async function subscribeToNewsletter(email) {
|
|
679
|
+
const apiUrl = getApiUrl();
|
|
680
|
+
const siteId = getSiteId();
|
|
681
|
+
const response = await fetch(`${apiUrl}/api/websites/${siteId}/subscribe`, {
|
|
682
|
+
method: "POST",
|
|
683
|
+
headers: {
|
|
684
|
+
"Content-Type": "application/json"
|
|
685
|
+
},
|
|
686
|
+
body: JSON.stringify({
|
|
687
|
+
email,
|
|
688
|
+
emailOptIn: true,
|
|
689
|
+
smsOptIn: false
|
|
690
|
+
})
|
|
691
|
+
});
|
|
692
|
+
if (!response.ok) {
|
|
693
|
+
const error = await response.json().catch(() => ({ message: "Failed to subscribe" }));
|
|
694
|
+
throw new Error(error.message || "Failed to subscribe");
|
|
695
|
+
}
|
|
696
|
+
return response.json();
|
|
697
|
+
}
|
|
698
|
+
async function submitContactForm(data) {
|
|
699
|
+
const apiUrl = getApiUrl();
|
|
700
|
+
const siteId = getSiteId();
|
|
701
|
+
const response = await fetch(`${apiUrl}/api/websites/${siteId}/contact`, {
|
|
702
|
+
method: "POST",
|
|
703
|
+
headers: {
|
|
704
|
+
"Content-Type": "application/json"
|
|
705
|
+
},
|
|
706
|
+
body: JSON.stringify(data)
|
|
707
|
+
});
|
|
708
|
+
if (!response.ok) {
|
|
709
|
+
const error = await response.json().catch(() => ({ message: "Failed to submit form" }));
|
|
710
|
+
throw new Error(error.message || "Failed to submit form");
|
|
711
|
+
}
|
|
712
|
+
return response.json();
|
|
713
|
+
}
|
|
714
|
+
async function getSiteMetadata() {
|
|
715
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
716
|
+
return runtimeConfig || null;
|
|
717
|
+
}
|
|
718
|
+
export {
|
|
719
|
+
clearImageCache,
|
|
720
|
+
contentRegistry,
|
|
721
|
+
getAllContent,
|
|
722
|
+
getCacheStats,
|
|
723
|
+
getCachedUrl,
|
|
724
|
+
getContent,
|
|
725
|
+
getSiteMetadata,
|
|
726
|
+
hasCachedImage,
|
|
727
|
+
hasContent,
|
|
728
|
+
initBuilderSelection,
|
|
729
|
+
initImageCacheListener,
|
|
730
|
+
registerContent,
|
|
731
|
+
resolveAssetUrl,
|
|
732
|
+
setAssetResolver,
|
|
733
|
+
setCachedImages,
|
|
734
|
+
submitContactForm,
|
|
735
|
+
subscribeToNewsletter
|
|
736
|
+
};
|