@salesforce/ui-design-mode 10.2.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.
Files changed (60) hide show
  1. package/LICENSE.txt +82 -0
  2. package/dist/protocol/domAttributes.cjs +6 -0
  3. package/dist/protocol/domAttributes.d.cts +18 -0
  4. package/dist/protocol/domAttributes.d.ts +18 -0
  5. package/dist/protocol/domAttributes.d.ts.map +1 -0
  6. package/dist/protocol/domAttributes.js +6 -0
  7. package/dist/protocol/index.cjs +14 -0
  8. package/dist/protocol/index.d.cts +8 -0
  9. package/dist/protocol/index.d.ts +8 -0
  10. package/dist/protocol/index.d.ts.map +1 -0
  11. package/dist/protocol/index.js +14 -0
  12. package/dist/protocol/messageTypes.cjs +18 -0
  13. package/dist/protocol/messageTypes.d.cts +83 -0
  14. package/dist/protocol/messageTypes.d.ts +83 -0
  15. package/dist/protocol/messageTypes.d.ts.map +1 -0
  16. package/dist/protocol/messageTypes.js +18 -0
  17. package/dist/runtime/design-mode-interactions.js +842 -0
  18. package/dist/runtime/index.cjs +15 -0
  19. package/dist/runtime/index.d.cts +11 -0
  20. package/dist/runtime/index.d.ts +11 -0
  21. package/dist/runtime/index.d.ts.map +1 -0
  22. package/dist/runtime/index.js +14 -0
  23. package/dist/runtime/interactions/communicationManager.d.cts +57 -0
  24. package/dist/runtime/interactions/communicationManager.d.ts +57 -0
  25. package/dist/runtime/interactions/communicationManager.d.ts.map +1 -0
  26. package/dist/runtime/interactions/componentMatcher.d.cts +39 -0
  27. package/dist/runtime/interactions/componentMatcher.d.ts +39 -0
  28. package/dist/runtime/interactions/componentMatcher.d.ts.map +1 -0
  29. package/dist/runtime/interactions/editableManager.d.cts +25 -0
  30. package/dist/runtime/interactions/editableManager.d.ts +25 -0
  31. package/dist/runtime/interactions/editableManager.d.ts.map +1 -0
  32. package/dist/runtime/interactions/eventHandlers.d.cts +40 -0
  33. package/dist/runtime/interactions/eventHandlers.d.ts +40 -0
  34. package/dist/runtime/interactions/eventHandlers.d.ts.map +1 -0
  35. package/dist/runtime/interactions/index.d.cts +7 -0
  36. package/dist/runtime/interactions/index.d.ts +7 -0
  37. package/dist/runtime/interactions/index.d.ts.map +1 -0
  38. package/dist/runtime/interactions/interactionsController.d.cts +53 -0
  39. package/dist/runtime/interactions/interactionsController.d.ts +53 -0
  40. package/dist/runtime/interactions/interactionsController.d.ts.map +1 -0
  41. package/dist/runtime/interactions/styleManager.d.cts +49 -0
  42. package/dist/runtime/interactions/styleManager.d.ts +49 -0
  43. package/dist/runtime/interactions/styleManager.d.ts.map +1 -0
  44. package/dist/runtime/interactions/utils/cssUtils.d.cts +54 -0
  45. package/dist/runtime/interactions/utils/cssUtils.d.ts +54 -0
  46. package/dist/runtime/interactions/utils/cssUtils.d.ts.map +1 -0
  47. package/dist/runtime/interactions/utils/sourceUtils.d.cts +28 -0
  48. package/dist/runtime/interactions/utils/sourceUtils.d.ts +28 -0
  49. package/dist/runtime/interactions/utils/sourceUtils.d.ts.map +1 -0
  50. package/dist/source-locator/react/babel/reactDesignTimeLocatorBabelPlugin.cjs +90 -0
  51. package/dist/source-locator/react/babel/reactDesignTimeLocatorBabelPlugin.d.cts +13 -0
  52. package/dist/source-locator/react/babel/reactDesignTimeLocatorBabelPlugin.d.ts +13 -0
  53. package/dist/source-locator/react/babel/reactDesignTimeLocatorBabelPlugin.d.ts.map +1 -0
  54. package/dist/source-locator/react/babel/reactDesignTimeLocatorBabelPlugin.js +90 -0
  55. package/dist/source-locator/react/index.cjs +5 -0
  56. package/dist/source-locator/react/index.d.cts +4 -0
  57. package/dist/source-locator/react/index.d.ts +4 -0
  58. package/dist/source-locator/react/index.d.ts.map +1 -0
  59. package/dist/source-locator/react/index.js +5 -0
  60. package/package.json +70 -0
@@ -0,0 +1,842 @@
1
+ /**
2
+ * Design Mode Interactions (Bundled)
3
+ *
4
+ * Auto-generated by esbuild from the modular design-mode interactions files.
5
+ * Do not edit this file directly - edit the modules in src/runtime/interactions/.
6
+ */
7
+ (function () {
8
+
9
+ "use strict";
10
+ (() => {
11
+ var __defProp = Object.defineProperty;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
14
+
15
+ // src/protocol/messageTypes.ts
16
+ var MSG_ENABLE_INTERACTIONS = "enable-interactions";
17
+ var MSG_DISABLE_INTERACTIONS = "disable-interactions";
18
+ var MSG_STYLE_CHANGE = "style-change";
19
+ var MSG_TEXT_CHANGE = "text-change";
20
+ var MSG_MEDIA_CHANGE = "media-change";
21
+ var MSG_INTERACTIONS_INITIALIZED = "interactions-initialized";
22
+ var MSG_COMPONENT_SELECTED = "component-selected";
23
+ var MSG_TEXT_CHANGED = "text-changed";
24
+
25
+ // src/protocol/domAttributes.ts
26
+ var ATTR_SOURCE_FILE = "data-source-file";
27
+
28
+ // src/runtime/interactions/utils/sourceUtils.ts
29
+ function parseOptionalInt(value) {
30
+ if (value === null || value === void 0 || value === "") {
31
+ return null;
32
+ }
33
+ const parsed = Number.parseInt(String(value), 10);
34
+ return Number.isFinite(parsed) ? parsed : null;
35
+ }
36
+ function parseSourceFileAttribute(value) {
37
+ const match = /^(.*):(\d+):(\d+)$/.exec(value);
38
+ if (!match) {
39
+ return { sourceFile: value, lineNumber: null, columnNumber: null };
40
+ }
41
+ return {
42
+ sourceFile: match[1] ?? value,
43
+ lineNumber: parseOptionalInt(match[2]),
44
+ columnNumber: parseOptionalInt(match[3])
45
+ };
46
+ }
47
+ function getSourceFromDataAttributes(element) {
48
+ if (!element) {
49
+ return null;
50
+ }
51
+ const source = element.getAttribute(ATTR_SOURCE_FILE) || null;
52
+ if (!source) {
53
+ return null;
54
+ }
55
+ return parseSourceFileAttribute(source);
56
+ }
57
+ function findElementsBySourceLocation(location) {
58
+ const results = [];
59
+ const elements = document.querySelectorAll(`[${ATTR_SOURCE_FILE}]`);
60
+ for (const el of elements) {
61
+ const elSource = getSourceFromDataAttributes(el);
62
+ if (elSource && elSource.sourceFile === location.sourceFile && elSource.lineNumber === location.lineNumber && elSource.columnNumber === location.columnNumber) {
63
+ results.push(el);
64
+ }
65
+ }
66
+ return results;
67
+ }
68
+ function parseSourceLocation(sl) {
69
+ if (!sl || typeof sl !== "object") return void 0;
70
+ const obj = sl;
71
+ return {
72
+ sourceFile: String(obj.sourceFile ?? ""),
73
+ lineNumber: typeof obj.lineNumber === "number" ? obj.lineNumber : null,
74
+ columnNumber: typeof obj.columnNumber === "number" ? obj.columnNumber : null
75
+ };
76
+ }
77
+ function getLabelFromSource(element) {
78
+ if (!element) {
79
+ return "";
80
+ }
81
+ const source = element.getAttribute(ATTR_SOURCE_FILE);
82
+ if (!source) {
83
+ return element.tagName ? element.tagName.toLowerCase() : "";
84
+ }
85
+ const { sourceFile } = parseSourceFileAttribute(source);
86
+ const parts = sourceFile.split(/[/\\]/);
87
+ const baseName = parts[parts.length - 1] || sourceFile;
88
+ return baseName;
89
+ }
90
+
91
+ // src/runtime/interactions/editableManager.ts
92
+ var TEXT_TAGS = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
93
+ var EditableManager = class {
94
+ constructor(communicationManager) {
95
+ __publicField(this, "communicationManager");
96
+ __publicField(this, "boundHandleBlur");
97
+ __publicField(this, "boundHandleKeydown");
98
+ __publicField(this, "boundHandleInput");
99
+ __publicField(this, "editableGroup");
100
+ this.communicationManager = communicationManager;
101
+ this.boundHandleBlur = this._handleBlur.bind(this);
102
+ this.boundHandleKeydown = this._handleKeydown.bind(this);
103
+ this.boundHandleInput = this._handleInput.bind(this);
104
+ this.editableGroup = [];
105
+ }
106
+ makeEditableIfText(element) {
107
+ if (!this._isTextElement(element)) {
108
+ return;
109
+ }
110
+ const source = getSourceFromDataAttributes(element);
111
+ const siblings = source ? findElementsBySourceLocation(source) : [];
112
+ const others = siblings.filter((el) => el !== element);
113
+ this.editableGroup = [element, ...others];
114
+ for (const el of this.editableGroup) {
115
+ el.dataset.originalText = el.textContent ?? "";
116
+ }
117
+ element.contentEditable = "true";
118
+ element.addEventListener("blur", this.boundHandleBlur);
119
+ element.addEventListener("keydown", this.boundHandleKeydown);
120
+ element.addEventListener("input", this.boundHandleInput);
121
+ }
122
+ removeEditable(element) {
123
+ if (element.contentEditable === "true") {
124
+ element.contentEditable = "false";
125
+ }
126
+ element.removeEventListener("blur", this.boundHandleBlur);
127
+ element.removeEventListener("keydown", this.boundHandleKeydown);
128
+ element.removeEventListener("input", this.boundHandleInput);
129
+ for (const el of this.editableGroup) {
130
+ delete el.dataset.originalText;
131
+ }
132
+ this.editableGroup = [];
133
+ }
134
+ _isTextElement(element) {
135
+ return TEXT_TAGS.includes(element.tagName) && (element.textContent ?? "").trim().length > 0 && element.dataset.textType === "static";
136
+ }
137
+ _handleInput(e) {
138
+ const primary = e.target;
139
+ const text = primary.textContent ?? "";
140
+ for (let i = 1; i < this.editableGroup.length; i++) {
141
+ this.editableGroup[i].textContent = text;
142
+ }
143
+ }
144
+ _handleBlur(e) {
145
+ const element = e.target;
146
+ const newText = element.textContent ?? "";
147
+ const originalText = element.dataset.originalText ?? "";
148
+ if (newText !== originalText) {
149
+ for (const el of this.editableGroup) {
150
+ el.dataset.originalText = newText;
151
+ }
152
+ if (this.communicationManager) {
153
+ this.communicationManager.notifyTextChange(element, originalText, newText);
154
+ }
155
+ }
156
+ this.removeEditable(element);
157
+ }
158
+ _handleKeydown(e) {
159
+ const element = e.target;
160
+ if (e.key === "Enter" && !e.shiftKey) {
161
+ e.preventDefault();
162
+ element.blur();
163
+ }
164
+ if (e.key === "Escape") {
165
+ for (const el of this.editableGroup) {
166
+ if (el.dataset.originalText) {
167
+ el.textContent = el.dataset.originalText;
168
+ }
169
+ }
170
+ element.blur();
171
+ }
172
+ }
173
+ };
174
+
175
+ // src/runtime/interactions/utils/cssUtils.ts
176
+ function getElementStyles(element) {
177
+ if (!element) return {};
178
+ const computed = window.getComputedStyle(element);
179
+ const inlineStyle = element.style;
180
+ const prop = (name, fallback) => ({
181
+ inline: inlineStyle[name] || (fallback ? inlineStyle[fallback] : "") || "",
182
+ computed: (fallback ? computed[fallback] : computed[name]) || ""
183
+ });
184
+ const box = (shorthand, top, right, bottom, left) => {
185
+ let inlineValue = inlineStyle[shorthand] || "";
186
+ if (!inlineValue) {
187
+ const iT = inlineStyle[top] || "", iR = inlineStyle[right] || "", iB = inlineStyle[bottom] || "", iL = inlineStyle[left] || "";
188
+ if (iT || iR || iB || iL) {
189
+ inlineValue = `${iT || "0px"} ${iR || "0px"} ${iB || "0px"} ${iL || "0px"}`;
190
+ }
191
+ }
192
+ return {
193
+ inline: inlineValue,
194
+ computed: `${computed[top] || ""} ${computed[right] || ""} ${computed[bottom] || ""} ${computed[left] || ""}`
195
+ };
196
+ };
197
+ return {
198
+ width: prop("width"),
199
+ minWidth: prop("minWidth"),
200
+ maxWidth: prop("maxWidth"),
201
+ height: prop("height"),
202
+ minHeight: prop("minHeight"),
203
+ maxHeight: prop("maxHeight"),
204
+ overflow: prop("overflow"),
205
+ padding: box("padding", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft"),
206
+ margin: box("margin", "marginTop", "marginRight", "marginBottom", "marginLeft"),
207
+ backgroundColor: prop("backgroundColor"),
208
+ borderWidth: box(
209
+ "borderWidth",
210
+ "borderTopWidth",
211
+ "borderRightWidth",
212
+ "borderBottomWidth",
213
+ "borderLeftWidth"
214
+ ),
215
+ borderStyle: prop("borderStyle", "borderTopStyle"),
216
+ borderColor: prop("borderColor", "borderTopColor"),
217
+ borderRadius: box(
218
+ "borderRadius",
219
+ "borderTopLeftRadius",
220
+ "borderTopRightRadius",
221
+ "borderBottomRightRadius",
222
+ "borderBottomLeftRadius"
223
+ ),
224
+ color: prop("color"),
225
+ fontFamily: prop("fontFamily"),
226
+ fontSize: prop("fontSize"),
227
+ fontWeight: prop("fontWeight"),
228
+ fontStyle: prop("fontStyle"),
229
+ lineHeight: prop("lineHeight"),
230
+ letterSpacing: prop("letterSpacing"),
231
+ textAlign: prop("textAlign"),
232
+ textDecoration: prop("textDecoration", "textDecorationLine"),
233
+ textTransform: prop("textTransform")
234
+ };
235
+ }
236
+
237
+ // src/runtime/interactions/communicationManager.ts
238
+ function getMediaInfo(element) {
239
+ switch (element.tagName) {
240
+ case "IMG": {
241
+ const img = element;
242
+ return {
243
+ tag: "img",
244
+ src: img.getAttribute("src") ?? "",
245
+ alt: img.getAttribute("alt") ?? "",
246
+ width: img.getAttribute("width") ?? "",
247
+ height: img.getAttribute("height") ?? ""
248
+ };
249
+ }
250
+ default:
251
+ return void 0;
252
+ }
253
+ }
254
+ var CommunicationManager = class {
255
+ constructor() {
256
+ }
257
+ /**
258
+ * Notify the extension about a selected component
259
+ * @param element - The selected element
260
+ */
261
+ notifyComponentSelected(element) {
262
+ const label = getLabelFromSource(element);
263
+ const wasSelected = element.classList.contains("design-mode-selected");
264
+ if (wasSelected) {
265
+ element.classList.remove("design-mode-selected");
266
+ }
267
+ const styles = getElementStyles(element);
268
+ if (wasSelected) {
269
+ element.classList.add("design-mode-selected");
270
+ }
271
+ const debugSource = getSourceFromDataAttributes(element);
272
+ const textType = element.dataset?.textType ?? "none";
273
+ const hasNonEditableText = TEXT_TAGS.includes(element.tagName) && (textType === "dynamic" || textType === "mixed");
274
+ const media = getMediaInfo(element);
275
+ try {
276
+ if (window.parent !== window) {
277
+ window.parent.postMessage(
278
+ {
279
+ type: MSG_COMPONENT_SELECTED,
280
+ component: {
281
+ name: label,
282
+ tagName: element.tagName,
283
+ styles: {
284
+ ...styles
285
+ },
286
+ debugSource,
287
+ hasNonEditableText,
288
+ ...media ? { media } : {}
289
+ }
290
+ },
291
+ "*"
292
+ );
293
+ }
294
+ } catch (error) {
295
+ console.log("Could not notify extension:", error);
296
+ }
297
+ window.selectedComponentInfo = {
298
+ name: label,
299
+ element,
300
+ tagName: element.tagName
301
+ };
302
+ }
303
+ /**
304
+ * Notify the extension about a text change
305
+ * @param element - The element that changed
306
+ * @param originalText - The original text
307
+ * @param newText - The new text
308
+ */
309
+ notifyTextChange(element, originalText, newText) {
310
+ const label = getLabelFromSource(element);
311
+ const debugSource = getSourceFromDataAttributes(element);
312
+ try {
313
+ if (window.parent !== window) {
314
+ window.parent.postMessage(
315
+ {
316
+ type: MSG_TEXT_CHANGED,
317
+ change: {
318
+ componentName: label,
319
+ tagName: element.tagName,
320
+ originalText,
321
+ newText,
322
+ debugSource
323
+ }
324
+ },
325
+ "*"
326
+ );
327
+ }
328
+ } catch (error) {
329
+ console.log("Could not notify extension about text change:", error);
330
+ }
331
+ }
332
+ /**
333
+ * Notify the parent window that interactions initialization is complete
334
+ */
335
+ notifyInitializationComplete() {
336
+ try {
337
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
338
+ window.parent.postMessage(
339
+ {
340
+ type: MSG_INTERACTIONS_INITIALIZED
341
+ },
342
+ "*"
343
+ );
344
+ }
345
+ } catch (error) {
346
+ const err = error;
347
+ console.warn("Could not send initialization message to parent:", err.message);
348
+ }
349
+ }
350
+ };
351
+
352
+ // src/runtime/interactions/componentMatcher.ts
353
+ var ComponentMatcher = class {
354
+ constructor(options = {}) {
355
+ __publicField(this, "allowlist");
356
+ this.allowlist = options.allowlist ?? [];
357
+ }
358
+ /**
359
+ * Check whether an element contains compile-time source metadata attributes.
360
+ * @param element - The element to check
361
+ * @returns True if the data-source-file attribute is present
362
+ */
363
+ hasSourceMetadata(element) {
364
+ if (!element) {
365
+ return false;
366
+ }
367
+ return element.hasAttribute(ATTR_SOURCE_FILE);
368
+ }
369
+ matchesList(element, selectors) {
370
+ return selectors.some((selector) => element.matches(selector));
371
+ }
372
+ /**
373
+ * Checks if a label represents a component name (not a fallback like tag name, ID, or text content)
374
+ * @param label - The label to check
375
+ * @param tagName - The element's tag name (lowercase)
376
+ * @returns True if the label is a component name
377
+ */
378
+ isComponentNameLabel(label, tagName) {
379
+ return label !== tagName && !label.includes("#") && !label.includes("(") && !label.includes('"');
380
+ }
381
+ /**
382
+ * Check if an element is highlightable.
383
+ * @param element - The element to check
384
+ * @returns True if the element should be highlighted
385
+ */
386
+ isHighlightableElement(element) {
387
+ if (!element || element === document.body || element === document.documentElement) {
388
+ return false;
389
+ }
390
+ if (!this.hasSourceMetadata(element)) {
391
+ return false;
392
+ }
393
+ if (this.allowlist.length > 0) {
394
+ return this.matchesList(element, this.allowlist);
395
+ }
396
+ return true;
397
+ }
398
+ /**
399
+ * Find the nearest highlightable element by walking up the DOM tree
400
+ * @param target - The target element
401
+ * @returns The highlightable element or null
402
+ */
403
+ findHighlightableElement(target) {
404
+ if (!target || target === document.body || target === document.documentElement) {
405
+ return null;
406
+ }
407
+ const closest = typeof target.closest === "function" ? target.closest(`[${ATTR_SOURCE_FILE}]`) : null;
408
+ if (closest && closest !== document.body && closest !== document.documentElement && this.isHighlightableElement(closest)) {
409
+ return closest;
410
+ }
411
+ let current = target;
412
+ while (current && current !== document.body && current !== document.documentElement) {
413
+ if (this.isHighlightableElement(current)) {
414
+ return current;
415
+ }
416
+ current = current.parentElement;
417
+ }
418
+ return null;
419
+ }
420
+ };
421
+
422
+ // src/runtime/interactions/eventHandlers.ts
423
+ var EventHandlers = class {
424
+ constructor(isInteractionsActive, componentMatcher, styleManager, editableManager, communicationManager) {
425
+ __publicField(this, "isInteractionsActive");
426
+ __publicField(this, "componentMatcher");
427
+ __publicField(this, "styleManager");
428
+ __publicField(this, "editableManager");
429
+ __publicField(this, "communicationManager");
430
+ __publicField(this, "currentHighlightedElements");
431
+ __publicField(this, "selectedElement");
432
+ __publicField(this, "selectedElements");
433
+ this.isInteractionsActive = isInteractionsActive;
434
+ this.componentMatcher = componentMatcher;
435
+ this.styleManager = styleManager;
436
+ this.editableManager = editableManager;
437
+ this.communicationManager = communicationManager;
438
+ this.currentHighlightedElements = [];
439
+ this.selectedElement = null;
440
+ this.selectedElements = [];
441
+ this.handleMouseOver = this.handleMouseOver.bind(this);
442
+ this.handleMouseLeave = this.handleMouseLeave.bind(this);
443
+ this.handleClick = this.handleClick.bind(this);
444
+ }
445
+ _findHighlightableElement(target) {
446
+ return this.componentMatcher.findHighlightableElement(target);
447
+ }
448
+ handleMouseOver(e) {
449
+ if (!this.isInteractionsActive()) {
450
+ return;
451
+ }
452
+ e.stopPropagation();
453
+ const target = e.target;
454
+ if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
455
+ return;
456
+ }
457
+ const element = this._findHighlightableElement(target);
458
+ if (!element) {
459
+ return;
460
+ }
461
+ if (this.currentHighlightedElements.includes(element) || this.selectedElement && this.selectedElement === element) {
462
+ return;
463
+ }
464
+ if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
465
+ this.styleManager.unhighlightElements(this.currentHighlightedElements);
466
+ this.currentHighlightedElements = [];
467
+ }
468
+ const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
469
+ this.styleManager.highlightElements(allElements);
470
+ this.currentHighlightedElements = allElements;
471
+ }
472
+ handleMouseLeave() {
473
+ if (!this.isInteractionsActive()) {
474
+ return;
475
+ }
476
+ if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
477
+ this.styleManager.unhighlightElements(this.currentHighlightedElements);
478
+ this.currentHighlightedElements = [];
479
+ }
480
+ }
481
+ handleClick(e) {
482
+ if (!this.isInteractionsActive()) {
483
+ return;
484
+ }
485
+ e.preventDefault();
486
+ e.stopPropagation();
487
+ const target = e.target;
488
+ if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
489
+ return;
490
+ }
491
+ const element = this._findHighlightableElement(target);
492
+ if (!element) {
493
+ return;
494
+ }
495
+ if (this.selectedElement && this.selectedElement === element) {
496
+ return;
497
+ }
498
+ if (this.selectedElement) {
499
+ this.styleManager.deselectElements(this.selectedElements);
500
+ this.editableManager.removeEditable(this.selectedElement);
501
+ }
502
+ if (this.currentHighlightedElements.length > 0) {
503
+ this.styleManager.unhighlightElements(this.currentHighlightedElements);
504
+ this.currentHighlightedElements = [];
505
+ }
506
+ this.selectedElement = element;
507
+ const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
508
+ this.selectedElements = allElements;
509
+ this.styleManager.selectElements(allElements);
510
+ this.editableManager.makeEditableIfText(element);
511
+ this.communicationManager.notifyComponentSelected(element);
512
+ }
513
+ clearAll() {
514
+ if (this.currentHighlightedElements.length > 0) {
515
+ this.styleManager.unhighlightElements(this.currentHighlightedElements);
516
+ this.currentHighlightedElements = [];
517
+ }
518
+ if (this.selectedElement) {
519
+ this.styleManager.deselectElements(this.selectedElements);
520
+ this.editableManager.removeEditable(this.selectedElement);
521
+ this.selectedElement = null;
522
+ this.selectedElements = [];
523
+ }
524
+ }
525
+ getSelectedElement() {
526
+ return this.selectedElement;
527
+ }
528
+ };
529
+
530
+ // src/runtime/interactions/styleManager.ts
531
+ var StyleManager = class {
532
+ constructor() {
533
+ __publicField(this, "styleId");
534
+ __publicField(this, "stylesAdded");
535
+ this.styleId = "design-mode-styles";
536
+ this.stylesAdded = false;
537
+ }
538
+ /**
539
+ * Add CSS styles for highlighting to the document
540
+ */
541
+ addHighlightStyles() {
542
+ if (this.stylesAdded) {
543
+ return;
544
+ }
545
+ const style = document.createElement("style");
546
+ style.id = this.styleId;
547
+ style.textContent = this._getStyles();
548
+ document.head.appendChild(style);
549
+ this.stylesAdded = true;
550
+ }
551
+ /**
552
+ * Remove highlight styles from the document
553
+ */
554
+ removeHighlightStyles() {
555
+ const style = document.getElementById(this.styleId);
556
+ if (style) {
557
+ style.remove();
558
+ this.stylesAdded = false;
559
+ }
560
+ }
561
+ /**
562
+ * Get the CSS styles for highlighting
563
+ * @private
564
+ * @returns CSS styles
565
+ */
566
+ _getStyles() {
567
+ return `
568
+ .design-mode-highlight {
569
+ outline: 4px dashed #007acc !important;
570
+ outline-offset: -4px !important;
571
+ transition: all 0.2s ease !important;
572
+ position: relative !important;
573
+ cursor: pointer !important;
574
+ }
575
+ .design-mode-selected {
576
+ outline: 5px dashed #ff6b35 !important;
577
+ outline-offset: -5px !important;
578
+ position: relative !important;
579
+ }
580
+ `;
581
+ }
582
+ /**
583
+ * Apply highlight class to one or more elements
584
+ * @param elements - The elements to highlight
585
+ */
586
+ highlightElements(elements) {
587
+ for (const el of elements) {
588
+ el.classList.add("design-mode-highlight");
589
+ }
590
+ }
591
+ /**
592
+ * Remove highlight class from one or more elements
593
+ * @param elements - The elements to unhighlight
594
+ */
595
+ unhighlightElements(elements) {
596
+ for (const el of elements) {
597
+ el.classList.remove("design-mode-highlight");
598
+ }
599
+ }
600
+ /**
601
+ * Apply selected class to one or more elements
602
+ * @param elements - The elements to select
603
+ */
604
+ selectElements(elements) {
605
+ for (const el of elements) {
606
+ el.classList.add("design-mode-selected");
607
+ }
608
+ }
609
+ /**
610
+ * Remove selected class from one or more elements
611
+ * @param elements - The elements to deselect
612
+ */
613
+ deselectElements(elements) {
614
+ for (const el of elements) {
615
+ el.classList.remove("design-mode-selected");
616
+ }
617
+ }
618
+ };
619
+
620
+ // src/runtime/interactions/interactionsController.ts
621
+ var mediaAppliers = {
622
+ img: (el, attrs) => {
623
+ const img = el;
624
+ if (attrs.src !== void 0) img.setAttribute("src", attrs.src);
625
+ if (attrs.alt !== void 0) img.setAttribute("alt", attrs.alt);
626
+ if (attrs.width !== void 0) {
627
+ if (attrs.width === "") img.removeAttribute("width");
628
+ else img.setAttribute("width", attrs.width);
629
+ }
630
+ if (attrs.height !== void 0) {
631
+ if (attrs.height === "") img.removeAttribute("height");
632
+ else img.setAttribute("height", attrs.height);
633
+ }
634
+ }
635
+ };
636
+ var InteractionsController = class {
637
+ constructor(enabled = true) {
638
+ __publicField(this, "enabled");
639
+ __publicField(this, "isActive");
640
+ __publicField(this, "componentMatcher");
641
+ __publicField(this, "styleManager");
642
+ __publicField(this, "communicationManager");
643
+ __publicField(this, "editableManager");
644
+ __publicField(this, "eventHandlers");
645
+ this.enabled = enabled;
646
+ this.isActive = false;
647
+ this.componentMatcher = new ComponentMatcher({
648
+ allowlist: [
649
+ "div",
650
+ "p",
651
+ "span",
652
+ "h1",
653
+ "h2",
654
+ "h3",
655
+ "h4",
656
+ "h5",
657
+ "h6",
658
+ "a",
659
+ "button",
660
+ "input",
661
+ "select",
662
+ "textarea",
663
+ "label",
664
+ "section",
665
+ "article",
666
+ "main",
667
+ "aside",
668
+ "header",
669
+ "footer",
670
+ "nav",
671
+ "figure",
672
+ "figcaption",
673
+ "ul",
674
+ "ol",
675
+ "li",
676
+ "table",
677
+ "tr",
678
+ "td",
679
+ "th",
680
+ "blockquote",
681
+ "img"
682
+ ]
683
+ });
684
+ this.styleManager = new StyleManager();
685
+ this.communicationManager = new CommunicationManager();
686
+ this.editableManager = new EditableManager(this.communicationManager);
687
+ this.eventHandlers = new EventHandlers(
688
+ () => this.isActive,
689
+ this.componentMatcher,
690
+ this.styleManager,
691
+ this.editableManager,
692
+ this.communicationManager
693
+ );
694
+ }
695
+ /**
696
+ * Initialize the design mode interactions
697
+ */
698
+ initialize() {
699
+ if (!this.enabled) {
700
+ console.log("Design Mode Interactions disabled");
701
+ return;
702
+ }
703
+ console.log("Initializing Design Mode Interactions...");
704
+ this.styleManager.addHighlightStyles();
705
+ document.addEventListener("mouseover", this.eventHandlers.handleMouseOver);
706
+ document.addEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
707
+ document.addEventListener("click", this.eventHandlers.handleClick, true);
708
+ console.log("Design Mode Interactions initialized!");
709
+ this.communicationManager.notifyInitializationComplete();
710
+ }
711
+ /**
712
+ * Enable the design mode interactions
713
+ */
714
+ enable() {
715
+ this.isActive = true;
716
+ console.log("Design Mode Interactions enabled");
717
+ }
718
+ /**
719
+ * Disable the design mode interactions
720
+ */
721
+ disable() {
722
+ this.isActive = false;
723
+ this.eventHandlers.clearAll();
724
+ console.log("Design Mode Interactions disabled");
725
+ }
726
+ resolveTargets(sourceLocation) {
727
+ let location = sourceLocation ?? null;
728
+ if (!location?.sourceFile) {
729
+ const selectedElement = this.eventHandlers.getSelectedElement();
730
+ location = getSourceFromDataAttributes(selectedElement);
731
+ }
732
+ return location ? findElementsBySourceLocation(location) : [];
733
+ }
734
+ /**
735
+ * Apply a style change to all elements at the given source location.
736
+ * When sourceLocation is provided (undo/redo), it is used directly.
737
+ * Otherwise the source location is read from the currently selected element.
738
+ */
739
+ applyStyleChange(property, value, sourceLocation) {
740
+ for (const el of this.resolveTargets(sourceLocation)) {
741
+ el.style[property] = value;
742
+ }
743
+ }
744
+ applyTextChange(text, sourceLocation) {
745
+ for (const el of this.resolveTargets(sourceLocation)) {
746
+ el.textContent = text;
747
+ }
748
+ }
749
+ /**
750
+ * Apply attribute changes to all elements at the given source location whose
751
+ * `tagName` matches `tag` (case-insensitive). Per-tag appliers declare which
752
+ * attributes are valid and how empty-string values are handled (e.g. for
753
+ * `img`, an empty `width`/`height` clears the attribute).
754
+ *
755
+ * No-op if the tag has no registered applier. When `sourceLocation` is
756
+ * omitted (live edit), the source is read from the currently selected
757
+ * element; when provided (undo/redo replay), it is used directly.
758
+ *
759
+ * To support a new tag, add an entry to {@link mediaAppliers}.
760
+ *
761
+ * @param tag - The HTML tag the change targets (e.g. `"img"`).
762
+ * @param attributes - Sparse map of attribute names to their new string values; `undefined` values are skipped.
763
+ * @param sourceLocation - Explicit source location for undo/redo; falls back to the selected element when omitted.
764
+ */
765
+ applyMediaChange(tag, attributes, sourceLocation) {
766
+ const applier = mediaAppliers[tag.toLowerCase()];
767
+ if (!applier) return;
768
+ const tagNameUpper = tag.toUpperCase();
769
+ for (const el of this.resolveTargets(sourceLocation)) {
770
+ if (el.tagName !== tagNameUpper) continue;
771
+ applier(el, attributes);
772
+ }
773
+ }
774
+ /**
775
+ * Cleanup and remove event listeners
776
+ */
777
+ destroy() {
778
+ document.removeEventListener("mouseover", this.eventHandlers.handleMouseOver);
779
+ document.removeEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
780
+ document.removeEventListener("click", this.eventHandlers.handleClick, true);
781
+ this.styleManager.removeHighlightStyles();
782
+ this.eventHandlers.clearAll();
783
+ }
784
+ };
785
+
786
+ // src/runtime/interactions/index.ts
787
+ var interactions = new InteractionsController(true);
788
+ if (typeof document !== "undefined") {
789
+ if (document.readyState === "loading") {
790
+ document.addEventListener("DOMContentLoaded", () => {
791
+ interactions.initialize();
792
+ });
793
+ } else {
794
+ interactions.initialize();
795
+ }
796
+ }
797
+ if (typeof window !== "undefined") {
798
+ window.enableInteractions = function() {
799
+ interactions.enable();
800
+ };
801
+ window.disableInteractions = function() {
802
+ interactions.disable();
803
+ };
804
+ window.addEventListener("message", function(event) {
805
+ const data = event.data;
806
+ const typed = data && typeof data === "object" ? data : null;
807
+ if (typed && typed.type === MSG_STYLE_CHANGE) {
808
+ interactions.applyStyleChange(
809
+ String(typed.property ?? ""),
810
+ String(typed.value ?? ""),
811
+ parseSourceLocation(typed.sourceLocation)
812
+ );
813
+ }
814
+ if (typed && typed.type === MSG_TEXT_CHANGE) {
815
+ interactions.applyTextChange(
816
+ String(typed.text ?? ""),
817
+ parseSourceLocation(typed.sourceLocation)
818
+ );
819
+ }
820
+ if (typed && typed.type === MSG_MEDIA_CHANGE) {
821
+ const tag = typeof typed.tag === "string" ? typed.tag : "";
822
+ const rawAttributes = typed.attributes && typeof typed.attributes === "object" ? typed.attributes : {};
823
+ const attributes = {};
824
+ for (const [key, raw] of Object.entries(rawAttributes)) {
825
+ attributes[key] = raw === void 0 ? void 0 : String(raw);
826
+ }
827
+ if (tag) {
828
+ interactions.applyMediaChange(tag, attributes, parseSourceLocation(typed.sourceLocation));
829
+ }
830
+ }
831
+ if (typed && typed.type === MSG_ENABLE_INTERACTIONS) {
832
+ window.enableInteractions?.();
833
+ }
834
+ if (typed && typed.type === MSG_DISABLE_INTERACTIONS) {
835
+ window.disableInteractions?.();
836
+ }
837
+ });
838
+ }
839
+ })();
840
+
841
+ })();
842
+