zuljaman-banner-components 1.0.22 → 1.0.24

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 (117) hide show
  1. package/README.md +228 -131
  2. package/dist/components/BannerRenderer/BannerRenderer.d.ts +8 -0
  3. package/dist/components/BannerRenderer/BannerRenderer.d.ts.map +1 -0
  4. package/dist/components/BannerRenderer/BannerRenderer.js +325 -0
  5. package/dist/components/BannerRenderer/components/CopyElement.d.ts +62 -0
  6. package/dist/components/BannerRenderer/components/CopyElement.d.ts.map +1 -0
  7. package/dist/components/BannerRenderer/components/CopyElement.js +220 -0
  8. package/dist/components/BannerRenderer/components/InteractiveLines.d.ts +26 -0
  9. package/dist/components/BannerRenderer/components/InteractiveLines.d.ts.map +1 -0
  10. package/dist/components/BannerRenderer/components/InteractiveLines.js +172 -0
  11. package/dist/components/BannerRenderer/components/LogoElement.d.ts +55 -0
  12. package/dist/components/BannerRenderer/components/LogoElement.d.ts.map +1 -0
  13. package/dist/components/BannerRenderer/components/LogoElement.js +53 -0
  14. package/dist/components/BannerRenderer/components/VisualGuides.d.ts +43 -0
  15. package/dist/components/BannerRenderer/components/VisualGuides.d.ts.map +1 -0
  16. package/dist/components/BannerRenderer/components/VisualGuides.js +110 -0
  17. package/dist/components/BannerRenderer/components/index.d.ts +12 -0
  18. package/dist/components/BannerRenderer/components/index.d.ts.map +1 -0
  19. package/dist/components/BannerRenderer/components/index.js +14 -0
  20. package/dist/components/BannerRenderer/constants.d.ts +15 -0
  21. package/dist/components/BannerRenderer/constants.d.ts.map +1 -0
  22. package/dist/components/BannerRenderer/constants.js +36 -0
  23. package/dist/components/BannerRenderer/hooks/index.d.ts +14 -0
  24. package/dist/components/BannerRenderer/hooks/index.d.ts.map +1 -0
  25. package/dist/components/BannerRenderer/hooks/index.js +16 -0
  26. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.d.ts +30 -0
  27. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.d.ts.map +1 -0
  28. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.js +87 -0
  29. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.d.ts +13 -0
  30. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.d.ts.map +1 -0
  31. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.js +21 -0
  32. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.d.ts +71 -0
  33. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.d.ts.map +1 -0
  34. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.js +151 -0
  35. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.d.ts +66 -0
  36. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.d.ts.map +1 -0
  37. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.js +332 -0
  38. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.d.ts +61 -0
  39. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.d.ts.map +1 -0
  40. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.js +180 -0
  41. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.d.ts +60 -0
  42. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.d.ts.map +1 -0
  43. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.js +178 -0
  44. package/dist/components/BannerRenderer/hooks/useAutoPositioning.d.ts +8 -0
  45. package/dist/components/BannerRenderer/hooks/useAutoPositioning.d.ts.map +1 -0
  46. package/dist/components/BannerRenderer/hooks/useAutoPositioning.js +12 -0
  47. package/dist/components/BannerRenderer/hooks/useDragSnap.d.ts +30 -0
  48. package/dist/components/BannerRenderer/hooks/useDragSnap.d.ts.map +1 -0
  49. package/dist/components/BannerRenderer/hooks/useDragSnap.js +90 -0
  50. package/dist/components/BannerRenderer/hooks/useElementRefs.d.ts +22 -0
  51. package/dist/components/BannerRenderer/hooks/useElementRefs.d.ts.map +1 -0
  52. package/dist/components/BannerRenderer/hooks/useElementRefs.js +70 -0
  53. package/dist/components/BannerRenderer/hooks/useLineDrawing.d.ts +40 -0
  54. package/dist/components/BannerRenderer/hooks/useLineDrawing.d.ts.map +1 -0
  55. package/dist/components/BannerRenderer/hooks/useLineDrawing.js +198 -0
  56. package/dist/components/BannerRenderer/hooks/useProximityDetection.d.ts +43 -0
  57. package/dist/components/BannerRenderer/hooks/useProximityDetection.d.ts.map +1 -0
  58. package/dist/components/BannerRenderer/hooks/useProximityDetection.js +491 -0
  59. package/dist/components/BannerRenderer/index.d.ts +6 -0
  60. package/dist/components/BannerRenderer/index.d.ts.map +1 -0
  61. package/dist/components/BannerRenderer/index.js +8 -0
  62. package/dist/components/{BannerRenderer.d.ts → BannerRenderer/types.d.ts} +29 -8
  63. package/dist/components/BannerRenderer/types.d.ts.map +1 -0
  64. package/dist/components/BannerRenderer/types.js +5 -0
  65. package/dist/components/BannerRenderer/utils/alignmentUtils.d.ts +38 -0
  66. package/dist/components/BannerRenderer/utils/alignmentUtils.d.ts.map +1 -0
  67. package/dist/components/BannerRenderer/utils/alignmentUtils.js +138 -0
  68. package/dist/components/BannerRenderer/utils/elementCheckUtils.d.ts +22 -0
  69. package/dist/components/BannerRenderer/utils/elementCheckUtils.d.ts.map +1 -0
  70. package/dist/components/BannerRenderer/utils/elementCheckUtils.js +37 -0
  71. package/dist/components/BannerRenderer/utils/elementIdUtils.d.ts +25 -0
  72. package/dist/components/BannerRenderer/utils/elementIdUtils.d.ts.map +1 -0
  73. package/dist/components/BannerRenderer/utils/elementIdUtils.js +56 -0
  74. package/dist/components/BannerRenderer/utils/fontUtils.d.ts +12 -0
  75. package/dist/components/BannerRenderer/utils/fontUtils.d.ts.map +1 -0
  76. package/dist/components/BannerRenderer/utils/fontUtils.js +26 -0
  77. package/dist/components/BannerRenderer/utils/graphUtils.d.ts +54 -0
  78. package/dist/components/BannerRenderer/utils/graphUtils.d.ts.map +1 -0
  79. package/dist/components/BannerRenderer/utils/graphUtils.js +138 -0
  80. package/dist/components/BannerRenderer/utils/index.d.ts +12 -0
  81. package/dist/components/BannerRenderer/utils/index.d.ts.map +1 -0
  82. package/dist/components/BannerRenderer/utils/index.js +27 -0
  83. package/dist/components/BannerRenderer/utils/snapUtils.d.ts +38 -0
  84. package/dist/components/BannerRenderer/utils/snapUtils.d.ts.map +1 -0
  85. package/dist/components/BannerRenderer/utils/snapUtils.js +109 -0
  86. package/dist/components/BannerVisor.d.ts.map +1 -1
  87. package/dist/components/BannerVisor.js +8 -1
  88. package/dist/components/index.js +1 -1
  89. package/dist/components/shared/DraggableElement.d.ts +11 -0
  90. package/dist/components/shared/DraggableElement.d.ts.map +1 -1
  91. package/dist/components/shared/DraggableElement.js +51 -51
  92. package/dist/components/styles/Style1/substyleConfig.d.ts.map +1 -1
  93. package/dist/components/styles/Style1/substyleConfig.js +53 -54
  94. package/dist/components/styles/Style2/substyleConfig.js +78 -78
  95. package/dist/components/styles/Style3/substyleConfig.d.ts.map +1 -1
  96. package/dist/components/styles/Style3/substyleConfig.js +40 -37
  97. package/dist/components/styles/Style4/substyleConfig.js +57 -57
  98. package/dist/components/styles/types/substyleTypes.d.ts +32 -15
  99. package/dist/components/styles/types/substyleTypes.d.ts.map +1 -1
  100. package/dist/components/styles/utils/chainValidation.d.ts +41 -0
  101. package/dist/components/styles/utils/chainValidation.d.ts.map +1 -0
  102. package/dist/components/styles/utils/chainValidation.js +148 -0
  103. package/dist/components/styles/utils/positioningUtils.d.ts +207 -11
  104. package/dist/components/styles/utils/positioningUtils.d.ts.map +1 -1
  105. package/dist/components/styles/utils/positioningUtils.js +520 -19
  106. package/dist/constants/characterLimits.d.ts +4 -16
  107. package/dist/constants/characterLimits.d.ts.map +1 -1
  108. package/dist/constants/characterLimits.js +28 -26
  109. package/dist/constants/styleConfigs.js +6 -6
  110. package/dist/styleConfig.d.ts +4 -4
  111. package/dist/styleConfig.d.ts.map +1 -1
  112. package/dist/styleConfig.js +8 -16
  113. package/dist/types.d.ts +42 -0
  114. package/dist/types.d.ts.map +1 -1
  115. package/package.json +1 -1
  116. package/dist/components/BannerRenderer.d.ts.map +0 -1
  117. package/dist/components/BannerRenderer.js +0 -559
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ /**
3
+ * useAutoPositioning hook - Main Orchestrator
4
+ *
5
+ * This is the main hook that orchestrates all auto-positioning functionality.
6
+ * It composes several sub-hooks for better organization and maintainability.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.useAutoPositioning = useAutoPositioning;
43
+ const react_1 = __importStar(require("react"));
44
+ const styleConfig_1 = require("../../../../styleConfig");
45
+ const debugPositions_1 = require("./debugPositions");
46
+ const useAnchorEdgeLocking_1 = require("./useAnchorEdgeLocking");
47
+ const useHeightCompensation_1 = require("./useHeightCompensation");
48
+ const useChainPositioning_1 = require("./useChainPositioning");
49
+ /**
50
+ * Main hook that orchestrates auto-positioning functionality.
51
+ */
52
+ function useAutoPositioning({ logoRef, copyRefs, bannerContainerRef, copies, sizeMultiplier, creationMode, completedLines, manualLines, lineGaps, setCompletedLines, setManualLines, setLineGaps, config, autoPositioningOverride, logoTranslateX, logoTranslateY, logoRotation, logoWidth, bannerStyle, onApplyAutoPositioning, onCopyPositionChange, onConnectionsDeleted, exposeDebugFunctions, areElementsOverlapping, areVerticallyAligned, configKey, }) {
53
+ // ============================================
54
+ // STATE
55
+ // ============================================
56
+ const [autoOffsets, setAutoOffsets] = (0, react_1.useState)({});
57
+ const [recalcTrigger, setRecalcTrigger] = (0, react_1.useState)(0);
58
+ const [anchorCompensation, setAnchorCompensation] = (0, react_1.useState)(0);
59
+ const [lineRecalcTrigger, setLineRecalcTrigger] = (0, react_1.useState)(0);
60
+ // Anchor edge-locking state
61
+ const [anchorEdgeConfig, setAnchorEdgeConfig] = (0, react_1.useState)(null);
62
+ const [targetHeights, setTargetHeights] = (0, react_1.useState)({});
63
+ // Refs for tracking previous state
64
+ const prevCompletedLinesRef = (0, react_1.useRef)(new Map());
65
+ const prevElementsInChainsRef = (0, react_1.useRef)(new Set());
66
+ const initialPositioningDoneRef = (0, react_1.useRef)(false);
67
+ // Flag to indicate configKey reset is in progress (prevents CHAIN-POS from resetting positions)
68
+ const configKeyResetInProgressRef = (0, react_1.useRef)(false);
69
+ // ============================================
70
+ // CONFIG KEY RESET
71
+ // ============================================
72
+ // Reset ALL state when configKey changes (style/substyle change)
73
+ // This ensures the library self-resets without requiring consumer to use key prop
74
+ // IMPORTANT: useLayoutEffect ensures reset happens BEFORE next paint (sync)
75
+ (0, react_1.useLayoutEffect)(() => {
76
+ if (!configKey)
77
+ return; // Skip if configKey not provided (backward compat)
78
+ console.log('🔧 AUTO-POS useLayoutEffect - configKey reset:', configKey);
79
+ // Set flag to prevent CHAIN-POS from resetting positions during configKey change
80
+ configKeyResetInProgressRef.current = true;
81
+ // Reset all useState values
82
+ setAutoOffsets({});
83
+ setRecalcTrigger(0);
84
+ setAnchorCompensation(0);
85
+ setLineRecalcTrigger(0);
86
+ setAnchorEdgeConfig(null);
87
+ setTargetHeights({});
88
+ // Reset all refs
89
+ prevCompletedLinesRef.current = new Map();
90
+ prevElementsInChainsRef.current = new Set();
91
+ initialPositioningDoneRef.current = false;
92
+ console.log('🔧 AUTO-POS useLayoutEffect - reset completado');
93
+ // Clear flag after microtask to allow ANCHOR-LOCK to position elements first
94
+ queueMicrotask(() => {
95
+ configKeyResetInProgressRef.current = false;
96
+ });
97
+ }, [configKey]); // eslint-disable-line react-hooks/exhaustive-deps
98
+ // ^ Intentionally only depend on configKey - we want to reset when config changes
99
+ // Get style configuration
100
+ const styleConfig = (0, styleConfig_1.getStyleConfig)(bannerStyle);
101
+ // ============================================
102
+ // MEMOIZED VALUES
103
+ // ============================================
104
+ // Helper to check if an element exists
105
+ const elementExists = react_1.default.useCallback((elementId) => {
106
+ if (elementId === 'logo')
107
+ return true;
108
+ if (elementId.startsWith('copy-')) {
109
+ const styleSlotStr = elementId.replace('copy-', '');
110
+ const styleSlot = parseInt(styleSlotStr, 10);
111
+ if (isNaN(styleSlot))
112
+ return false;
113
+ return copies.some(c => c.styleSlot === styleSlot) || styleSlot < copies.length;
114
+ }
115
+ return false;
116
+ }, [copies]);
117
+ // Derive effective auto-positioning config
118
+ const derivedAutoConfig = (0, react_1.useMemo)(() => {
119
+ let baseConfig;
120
+ if (creationMode) {
121
+ if (completedLines.size > 0) {
122
+ const links = [];
123
+ completedLines.forEach((toId, fromId) => {
124
+ const lineKey = `${fromId}->${toId}`;
125
+ const gapRem = lineGaps.get(lineKey);
126
+ links.push({
127
+ fromElement: fromId,
128
+ toElement: toId,
129
+ direction: 'down',
130
+ gapRem,
131
+ });
132
+ // Debug logs removed - were causing 800+ logs per session
133
+ // if (gapRem !== undefined) {
134
+ // console.log(`🔗 Link ${lineKey} using preserved gapRem: ${gapRem.toFixed(2)}`);
135
+ // }
136
+ });
137
+ baseConfig = { enabled: true, links };
138
+ }
139
+ else {
140
+ baseConfig = undefined;
141
+ }
142
+ }
143
+ else {
144
+ baseConfig = config.autoPositioningConfig;
145
+ }
146
+ // Filter out links that reference non-existent elements
147
+ if (baseConfig === null || baseConfig === void 0 ? void 0 : baseConfig.links) {
148
+ const validLinks = baseConfig.links.filter(link => {
149
+ const fromExists = elementExists(link.fromElement);
150
+ const toExists = elementExists(link.toElement);
151
+ return fromExists && toExists;
152
+ });
153
+ if (validLinks.length !== baseConfig.links.length) {
154
+ return { ...baseConfig, links: validLinks };
155
+ }
156
+ }
157
+ return baseConfig;
158
+ }, [creationMode, completedLines, lineGaps, config.autoPositioningConfig, elementExists]);
159
+ // Derive active links (unified Map for both modes)
160
+ const activeLinks = (0, react_1.useMemo)(() => {
161
+ var _a;
162
+ if (creationMode) {
163
+ return completedLines;
164
+ }
165
+ else if ((derivedAutoConfig === null || derivedAutoConfig === void 0 ? void 0 : derivedAutoConfig.enabled) && ((_a = derivedAutoConfig === null || derivedAutoConfig === void 0 ? void 0 : derivedAutoConfig.links) === null || _a === void 0 ? void 0 : _a.length) > 0) {
166
+ const linksMap = new Map();
167
+ derivedAutoConfig.links.forEach(link => {
168
+ linksMap.set(link.fromElement, link.toElement);
169
+ });
170
+ return linksMap;
171
+ }
172
+ return new Map();
173
+ }, [creationMode, completedLines, derivedAutoConfig]);
174
+ const hasActiveLinks = activeLinks.size > 0;
175
+ // ============================================
176
+ // BASIC EFFECTS
177
+ // ============================================
178
+ // Reset initial positioning flag when config changes
179
+ (0, react_1.useEffect)(() => {
180
+ initialPositioningDoneRef.current = false;
181
+ }, [derivedAutoConfig]);
182
+ // Trigger recalculation when style changes
183
+ (0, react_1.useEffect)(() => {
184
+ setRecalcTrigger(prev => prev + 1);
185
+ }, [bannerStyle]);
186
+ // Trigger line recalculation after text changes
187
+ (0, react_1.useLayoutEffect)(() => {
188
+ if (!creationMode)
189
+ return;
190
+ requestAnimationFrame(() => {
191
+ requestAnimationFrame(() => {
192
+ setLineRecalcTrigger(prev => prev + 1);
193
+ });
194
+ });
195
+ }, [copies.map(c => c.text).join('|'), anchorCompensation, recalcTrigger, creationMode]);
196
+ // Reset state when config changes
197
+ (0, react_1.useEffect)(() => {
198
+ console.log('📏 AUTO-POS - config changed, reseteando autoOffsets a {}');
199
+ setAutoOffsets({});
200
+ setAnchorCompensation(0);
201
+ }, [config]);
202
+ // ============================================
203
+ // DEBUG & APPLY FUNCTIONS
204
+ // ============================================
205
+ const debugPositions = react_1.default.useCallback(() => {
206
+ return (0, debugPositions_1.createDebugPositions)({
207
+ completedLines,
208
+ copies,
209
+ logoTranslateX,
210
+ logoTranslateY,
211
+ logoWidth,
212
+ config,
213
+ sizeMultiplier,
214
+ logoRef,
215
+ copyRefs,
216
+ bannerContainerRef,
217
+ })();
218
+ }, [completedLines, copies, logoTranslateX, logoTranslateY, logoWidth, config, sizeMultiplier, logoRef, copyRefs, bannerContainerRef]);
219
+ const applyAutoPositioningConfig = react_1.default.useCallback(() => {
220
+ const generatedConfig = debugPositions();
221
+ if (!generatedConfig)
222
+ return;
223
+ if (onApplyAutoPositioning && generatedConfig.autoPositioningConfig) {
224
+ onApplyAutoPositioning({
225
+ config: generatedConfig.autoPositioningConfig,
226
+ gapRem: generatedConfig.gapRem,
227
+ });
228
+ }
229
+ setAutoOffsets(prev => {
230
+ console.log('📏 AUTO-POS applyAutoPositioningConfig - prev autoOffsets:', prev);
231
+ const preserved = {};
232
+ const prevElements = prevElementsInChainsRef.current;
233
+ Object.entries(prev).forEach(([key, value]) => {
234
+ const keyNum = Number(key);
235
+ let copy = copies === null || copies === void 0 ? void 0 : copies.find(c => c.styleSlot === keyNum);
236
+ if (!copy && copies && keyNum >= 0 && keyNum < copies.length) {
237
+ copy = copies[keyNum];
238
+ }
239
+ if (copy) {
240
+ const copyStyleSlot = copy.styleSlot !== undefined ? copy.styleSlot : keyNum;
241
+ const elementId = `copy-${copyStyleSlot}`;
242
+ if (prevElements.has(elementId)) {
243
+ preserved[keyNum] = value;
244
+ }
245
+ }
246
+ });
247
+ console.log('📏 AUTO-POS applyAutoPositioningConfig - preserved autoOffsets:', preserved);
248
+ return preserved;
249
+ });
250
+ setAnchorCompensation(0);
251
+ setRecalcTrigger(prev => prev + 1);
252
+ }, [debugPositions, onApplyAutoPositioning, copies]);
253
+ // Expose debug functions
254
+ (0, react_1.useEffect)(() => {
255
+ if (exposeDebugFunctions) {
256
+ window.__debugBannerPositions = debugPositions;
257
+ window.__applyAutoPositioning = applyAutoPositioningConfig;
258
+ }
259
+ }, [debugPositions, exposeDebugFunctions, applyAutoPositioningConfig]);
260
+ // ============================================
261
+ // SUB-HOOKS
262
+ // ============================================
263
+ // Anchor edge locking and initial positioning
264
+ (0, useAnchorEdgeLocking_1.useAnchorEdgeLocking)({
265
+ activeLinks,
266
+ copies,
267
+ sizeMultiplier,
268
+ creationMode,
269
+ logoRef,
270
+ copyRefs,
271
+ bannerContainerRef,
272
+ derivedAutoConfig,
273
+ config,
274
+ onCopyPositionChange,
275
+ anchorEdgeConfig,
276
+ setAnchorEdgeConfig,
277
+ targetHeights,
278
+ setTargetHeights,
279
+ initialPositioningDoneRef,
280
+ });
281
+ // Height compensation for anchor and targets
282
+ (0, useHeightCompensation_1.useHeightCompensation)({
283
+ anchorEdgeConfig,
284
+ setAnchorEdgeConfig,
285
+ targetHeights,
286
+ setTargetHeights,
287
+ activeLinks,
288
+ hasActiveLinks,
289
+ copies,
290
+ sizeMultiplier,
291
+ creationMode,
292
+ logoRef,
293
+ copyRefs,
294
+ bannerContainerRef,
295
+ derivedAutoConfig,
296
+ config,
297
+ lineGaps,
298
+ onCopyPositionChange,
299
+ });
300
+ // Chain positioning and link lifecycle
301
+ (0, useChainPositioning_1.useChainPositioning)({
302
+ completedLines,
303
+ manualLines,
304
+ lineGaps,
305
+ setCompletedLines,
306
+ setLineGaps,
307
+ copies,
308
+ creationMode,
309
+ autoOffsets,
310
+ setAutoOffsets,
311
+ setAnchorCompensation,
312
+ onCopyPositionChange,
313
+ onConnectionsDeleted,
314
+ applyAutoPositioningConfig,
315
+ prevCompletedLinesRef,
316
+ prevElementsInChainsRef,
317
+ areElementsOverlapping,
318
+ areVerticallyAligned,
319
+ configKeyResetInProgressRef,
320
+ });
321
+ // ============================================
322
+ // RETURN
323
+ // ============================================
324
+ return {
325
+ autoOffsets,
326
+ anchorCompensation,
327
+ lineRecalcTrigger,
328
+ derivedAutoConfig,
329
+ debugPositions,
330
+ applyAutoPositioningConfig,
331
+ };
332
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * useChainPositioning hook
3
+ *
4
+ * Handles chain management: auto-apply positioning when links change,
5
+ * cleanup when links are deleted, and line validation.
6
+ */
7
+ import type { CopyConfig } from '../../../../types';
8
+ /**
9
+ * Parameters for useChainPositioning hook.
10
+ */
11
+ export interface UseChainPositioningParams {
12
+ /** Map of completed proximity lines (fromId → toId) */
13
+ completedLines: Map<string, string>;
14
+ /** Set of manually created line keys */
15
+ manualLines: Set<string>;
16
+ /** Map of line gaps (lineKey → gapRem) */
17
+ lineGaps: Map<string, number>;
18
+ /** Setter for completed lines */
19
+ setCompletedLines: React.Dispatch<React.SetStateAction<Map<string, string>>>;
20
+ /** Setter for line gaps */
21
+ setLineGaps: React.Dispatch<React.SetStateAction<Map<string, number>>>;
22
+ /** Array of copy configurations */
23
+ copies: CopyConfig[];
24
+ /** Whether in creation mode */
25
+ creationMode: boolean;
26
+ /** Record of auto-positioning offsets (styleSlot → offset) */
27
+ autoOffsets: Record<number, number>;
28
+ /** Setter for auto offsets */
29
+ setAutoOffsets: React.Dispatch<React.SetStateAction<Record<number, number>>>;
30
+ /** Setter for anchor compensation */
31
+ setAnchorCompensation: React.Dispatch<React.SetStateAction<number>>;
32
+ /** Callback to update copy position */
33
+ onCopyPositionChange?: (index: number, pos: {
34
+ x: number;
35
+ y: number;
36
+ }) => void;
37
+ /** Callback when connections are deleted */
38
+ onConnectionsDeleted?: () => void;
39
+ /** Function to apply auto-positioning configuration */
40
+ applyAutoPositioningConfig: () => void;
41
+ /** Ref to track previous completed lines state */
42
+ prevCompletedLinesRef: React.MutableRefObject<Map<string, string>>;
43
+ /** Ref to track previous elements in chains */
44
+ prevElementsInChainsRef: React.MutableRefObject<Set<string>>;
45
+ /** Function to check if two elements are overlapping */
46
+ areElementsOverlapping: (id1: string, id2: string) => boolean;
47
+ /** Function to check if two elements are vertically aligned */
48
+ areVerticallyAligned: (id1: string, id2: string) => boolean;
49
+ /** Ref to track if configKey reset is in progress (skip resetting positions) */
50
+ configKeyResetInProgressRef?: React.MutableRefObject<boolean>;
51
+ }
52
+ /**
53
+ * Hook that manages chain positioning and link lifecycle.
54
+ *
55
+ * Responsibilities:
56
+ * - Auto-apply positioning when new links are created
57
+ * - Handle link deletion and cleanup
58
+ * - Validate manual lines
59
+ */
60
+ export declare function useChainPositioning({ completedLines, manualLines, lineGaps, setCompletedLines, setLineGaps, copies, creationMode, autoOffsets, setAutoOffsets, setAnchorCompensation, onCopyPositionChange, onConnectionsDeleted, applyAutoPositioningConfig, prevCompletedLinesRef, prevElementsInChainsRef, areElementsOverlapping, areVerticallyAligned, configKeyResetInProgressRef, }: UseChainPositioningParams): void;
61
+ //# sourceMappingURL=useChainPositioning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChainPositioning.d.ts","sourceRoot":"","sources":["../../../../../src/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,wCAAwC;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,iCAAiC;IACjC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7E,2BAA2B;IAC3B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACvE,mCAAmC;IACnC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,+BAA+B;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7E,qCAAqC;IACrC,qBAAqB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,uCAAuC;IACvC,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9E,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,uDAAuD;IACvD,0BAA0B,EAAE,MAAM,IAAI,CAAC;IACvC,kDAAkD;IAClD,qBAAqB,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnE,+CAA+C;IAC/C,uBAAuB,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,wDAAwD;IACxD,sBAAsB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9D,+DAA+D;IAC/D,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5D,gFAAgF;IAChF,2BAA2B,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAC/D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,cAAc,EACd,WAAW,EACX,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,MAAM,EACN,YAAY,EACZ,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,GAC5B,EAAE,yBAAyB,GAAG,IAAI,CAyMlC"}
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ /**
3
+ * useChainPositioning hook
4
+ *
5
+ * Handles chain management: auto-apply positioning when links change,
6
+ * cleanup when links are deleted, and line validation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.useChainPositioning = useChainPositioning;
10
+ const react_1 = require("react");
11
+ const positioningUtils_1 = require("../../../styles/utils/positioningUtils");
12
+ /**
13
+ * Hook that manages chain positioning and link lifecycle.
14
+ *
15
+ * Responsibilities:
16
+ * - Auto-apply positioning when new links are created
17
+ * - Handle link deletion and cleanup
18
+ * - Validate manual lines
19
+ */
20
+ function useChainPositioning({ completedLines, manualLines, lineGaps, setCompletedLines, setLineGaps, copies, creationMode, autoOffsets, setAutoOffsets, setAnchorCompensation, onCopyPositionChange, onConnectionsDeleted, applyAutoPositioningConfig, prevCompletedLinesRef, prevElementsInChainsRef, areElementsOverlapping, areVerticallyAligned, configKeyResetInProgressRef, }) {
21
+ // Auto-apply positioning when new proximity lines are created
22
+ (0, react_1.useEffect)(() => {
23
+ if (!creationMode) {
24
+ prevCompletedLinesRef.current = completedLines;
25
+ return;
26
+ }
27
+ const prev = prevCompletedLinesRef.current;
28
+ if ((0, positioningUtils_1.mapsAreEqual)(prev, completedLines)) {
29
+ return;
30
+ }
31
+ const newLines = Array.from(completedLines.entries()).filter(([from, to]) => {
32
+ const prevTarget = prev.get(from);
33
+ return prevTarget !== to;
34
+ });
35
+ const deletedLines = Array.from(prev.entries()).filter(([from, prevTo]) => {
36
+ const currentTarget = completedLines.get(from);
37
+ return currentTarget !== prevTo;
38
+ });
39
+ // Build current elements in chains set
40
+ const elementsInChains = new Set();
41
+ completedLines.forEach((to, from) => {
42
+ elementsInChains.add(from);
43
+ elementsInChains.add(to);
44
+ });
45
+ const previousElementsInChains = prevElementsInChainsRef.current;
46
+ // Check if there are NEW elements in the chain
47
+ const hasNewElementsInChain = Array.from(elementsInChains).some(elementId => !previousElementsInChains.has(elementId));
48
+ // If new elements joined the chain OR new lines were created, apply auto-positioning
49
+ if (hasNewElementsInChain || newLines.length > 0) {
50
+ // Identify which elements are TARGETs
51
+ const targetElements = new Set();
52
+ completedLines.forEach((toId) => {
53
+ targetElements.add(toId);
54
+ });
55
+ // Reset translateY only for TARGET elements that JUST joined the chain
56
+ elementsInChains.forEach(elementId => {
57
+ const isNewToChain = !previousElementsInChains.has(elementId);
58
+ const isTarget = targetElements.has(elementId);
59
+ // Skip resetting y to 0 if configKey reset is in progress
60
+ // This prevents race condition where CHAIN-POS resets positions after ANCHOR-LOCK calculated them
61
+ if (isNewToChain && isTarget && elementId !== 'logo' && onCopyPositionChange && !(configKeyResetInProgressRef === null || configKeyResetInProgressRef === void 0 ? void 0 : configKeyResetInProgressRef.current)) {
62
+ const copyIndex = (0, positioningUtils_1.getCopyIndexFromElementId)(elementId, copies || []);
63
+ if (copyIndex !== undefined && copyIndex >= 0 && copyIndex < ((copies === null || copies === void 0 ? void 0 : copies.length) || 0)) {
64
+ const copy = copies[copyIndex];
65
+ console.log('⛓️ CHAIN-POS newToChain - copyIndex:', copyIndex, 'reseteando y a 0');
66
+ onCopyPositionChange(copyIndex, {
67
+ x: copy.translateX || 0,
68
+ y: 0,
69
+ });
70
+ }
71
+ }
72
+ });
73
+ applyAutoPositioningConfig();
74
+ }
75
+ // ALWAYS update the ref to track current chain members
76
+ prevElementsInChainsRef.current = new Set(elementsInChains);
77
+ // If lines were deleted, remove auto-positioning
78
+ if (deletedLines.length > 0) {
79
+ const targetElements = new Set(deletedLines.map(([, to]) => to));
80
+ targetElements.forEach(targetElementName => {
81
+ let styleSlot;
82
+ const match = targetElementName.match(/^copy-(\d+)$/);
83
+ if (match) {
84
+ styleSlot = parseInt(match[1], 10);
85
+ }
86
+ if (styleSlot !== undefined) {
87
+ let copyIndex = copies.findIndex(c => c.styleSlot === styleSlot);
88
+ if (copyIndex === -1) {
89
+ copyIndex = styleSlot;
90
+ }
91
+ if (copyIndex >= 0 && copyIndex < copies.length) {
92
+ const copy = copies[copyIndex];
93
+ const offsetKey = copy.styleSlot !== undefined ? copy.styleSlot : copyIndex;
94
+ const currentAutoOffset = autoOffsets[offsetKey];
95
+ if (currentAutoOffset !== undefined && onCopyPositionChange) {
96
+ const currentTranslateY = copy.translateY || 0;
97
+ const newTranslateY = currentTranslateY + currentAutoOffset;
98
+ console.log('⛓️ CHAIN-POS deleted line - copyIndex:', copyIndex, 'newTranslateY:', newTranslateY);
99
+ onCopyPositionChange(copyIndex, {
100
+ x: copy.translateX || 0,
101
+ y: newTranslateY,
102
+ });
103
+ }
104
+ }
105
+ }
106
+ });
107
+ // Build set of elements still in chains
108
+ const remainingElementsInChains = new Set();
109
+ completedLines.forEach((to, from) => {
110
+ remainingElementsInChains.add(from);
111
+ remainingElementsInChains.add(to);
112
+ });
113
+ setAutoOffsets(prev => {
114
+ const result = {};
115
+ Object.entries(prev).forEach(([key, value]) => {
116
+ const keyNum = Number(key);
117
+ let copy = copies === null || copies === void 0 ? void 0 : copies.find(c => c.styleSlot === keyNum);
118
+ if (!copy && copies && keyNum >= 0 && keyNum < copies.length) {
119
+ copy = copies[keyNum];
120
+ }
121
+ if (copy) {
122
+ const elementId = `copy-${copy.id}`;
123
+ if (remainingElementsInChains.has(elementId)) {
124
+ result[keyNum] = value;
125
+ }
126
+ }
127
+ });
128
+ return result;
129
+ });
130
+ setAnchorCompensation(0);
131
+ // Clean up gaps for deleted lines (only manual lines)
132
+ setLineGaps(prevGaps => {
133
+ const newGaps = new Map(prevGaps);
134
+ deletedLines.forEach(([from, to]) => {
135
+ const lineKey = `${from}->${to}`;
136
+ const isManualLine = manualLines.has(lineKey);
137
+ if (isManualLine) {
138
+ newGaps.delete(lineKey);
139
+ }
140
+ });
141
+ return newGaps;
142
+ });
143
+ if (onConnectionsDeleted) {
144
+ onConnectionsDeleted();
145
+ }
146
+ prevElementsInChainsRef.current = remainingElementsInChains;
147
+ }
148
+ prevCompletedLinesRef.current = new Map(completedLines);
149
+ }, [completedLines, creationMode, applyAutoPositioningConfig, copies, autoOffsets, onCopyPositionChange, onConnectionsDeleted, setLineGaps, manualLines, prevCompletedLinesRef, prevElementsInChainsRef, setAutoOffsets, setAnchorCompensation]);
150
+ // Validate and remove invalid lines automatically
151
+ (0, react_1.useEffect)(() => {
152
+ if (!creationMode)
153
+ return;
154
+ setCompletedLines(prev => {
155
+ const validLines = new Map();
156
+ let removedCount = 0;
157
+ prev.forEach((targetId, sourceId) => {
158
+ const lineKey = `${sourceId}->${targetId}`;
159
+ const isManualLine = manualLines.has(lineKey);
160
+ if (!isManualLine) {
161
+ validLines.set(sourceId, targetId);
162
+ return;
163
+ }
164
+ const isOverlapping = areElementsOverlapping(sourceId, targetId);
165
+ const isAligned = areVerticallyAligned(sourceId, targetId);
166
+ if (!isOverlapping && isAligned) {
167
+ validLines.set(sourceId, targetId);
168
+ }
169
+ else {
170
+ removedCount++;
171
+ }
172
+ });
173
+ if (removedCount > 0) {
174
+ return validLines;
175
+ }
176
+ return prev;
177
+ });
178
+ // eslint-disable-next-line react-hooks/exhaustive-deps
179
+ }, [creationMode, manualLines, setCompletedLines]);
180
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * useHeightCompensation hook
3
+ *
4
+ * Handles height compensation for anchor and target elements when their text changes.
5
+ * Maintains edge-locking by adjusting positions when element heights change.
6
+ */
7
+ import type { CopyConfig } from '../../../../types';
8
+ import type { SubstyleConfig, AutoPositioningConfig } from '../../../styles/types/substyleTypes';
9
+ import type { AnchorEdgeState } from './useAnchorEdgeLocking';
10
+ /**
11
+ * Parameters for useHeightCompensation hook.
12
+ */
13
+ export interface UseHeightCompensationParams {
14
+ /** Current anchor edge configuration */
15
+ anchorEdgeConfig: AnchorEdgeState | null;
16
+ /** Setter for anchor edge configuration */
17
+ setAnchorEdgeConfig: React.Dispatch<React.SetStateAction<AnchorEdgeState | null>>;
18
+ /** Record of target element heights (elementId → height) */
19
+ targetHeights: Record<string, number>;
20
+ /** Setter for target heights */
21
+ setTargetHeights: React.Dispatch<React.SetStateAction<Record<string, number>>>;
22
+ /** Map of active links (fromId → toId) */
23
+ activeLinks: Map<string, string>;
24
+ /** Whether there are any active links */
25
+ hasActiveLinks: boolean;
26
+ /** Array of copy configurations */
27
+ copies: CopyConfig[];
28
+ /** Current size multiplier for scaling */
29
+ sizeMultiplier: number;
30
+ /** Whether in creation mode */
31
+ creationMode: boolean;
32
+ /** Reference to the logo element */
33
+ logoRef: React.RefObject<HTMLDivElement | null>;
34
+ /** References to copy elements */
35
+ copyRefs: React.MutableRefObject<(HTMLDivElement | null)[]>;
36
+ /** Reference to the banner container */
37
+ bannerContainerRef: React.RefObject<HTMLDivElement | null>;
38
+ /** Derived auto-positioning configuration */
39
+ derivedAutoConfig: AutoPositioningConfig | undefined;
40
+ /** Substyle configuration */
41
+ config: SubstyleConfig;
42
+ /** Map of line gaps (lineKey → gapRem) */
43
+ lineGaps: Map<string, number>;
44
+ /** Callback to update copy position */
45
+ onCopyPositionChange?: (index: number, pos: {
46
+ x: number;
47
+ y: number;
48
+ }) => void;
49
+ }
50
+ /**
51
+ * Hook that manages height compensation for anchor and target elements.
52
+ *
53
+ * Responsibilities:
54
+ * - Adjust anchor position when its height changes (edge-locking)
55
+ * - Adjust target positions when anchor grows
56
+ * - Adjust target positions when they grow (self-compensation)
57
+ * - Move subsequent elements in chain when any element grows
58
+ */
59
+ export declare function useHeightCompensation({ anchorEdgeConfig, setAnchorEdgeConfig, targetHeights, setTargetHeights, activeLinks, hasActiveLinks, copies, sizeMultiplier, creationMode, logoRef, copyRefs, bannerContainerRef, derivedAutoConfig, config, lineGaps, onCopyPositionChange, }: UseHeightCompensationParams): void;
60
+ //# sourceMappingURL=useHeightCompensation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHeightCompensation.d.ts","sourceRoot":"","sources":["../../../../../src/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,wCAAwC;IACxC,gBAAgB,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,2CAA2C;IAC3C,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAClF,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,gCAAgC;IAChC,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/E,0CAA0C;IAC1C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,yCAAyC;IACzC,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,0CAA0C;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,oCAAoC;IACpC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,kCAAkC;IAClC,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,wCAAwC;IACxC,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC3D,6CAA6C;IAC7C,iBAAiB,EAAE,qBAAqB,GAAG,SAAS,CAAC;IACrD,6BAA6B;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uCAAuC;IACvC,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,MAAM,EACN,cAAc,EACd,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,QAAQ,EACR,oBAAoB,GACrB,EAAE,2BAA2B,GAAG,IAAI,CA4LpC"}