slicejs-web-framework 3.2.1 → 3.2.3

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 (107) hide show
  1. package/.opencode/opencode.json +14 -0
  2. package/LICENSE +21 -21
  3. package/README.md +174 -174
  4. package/Slice/Components/Structural/ContextManager/ContextManager.js +369 -369
  5. package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +297 -297
  6. package/Slice/Components/Structural/Controller/Controller.js +1138 -1129
  7. package/Slice/Components/Structural/Controller/allowedValuesValidation.js +52 -0
  8. package/Slice/Components/Structural/Debugger/Debugger.css +619 -619
  9. package/Slice/Components/Structural/Debugger/Debugger.html +72 -72
  10. package/Slice/Components/Structural/Debugger/Debugger.js +1547 -1547
  11. package/Slice/Components/Structural/EventManager/EventManager.js +338 -338
  12. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +361 -361
  13. package/Slice/Components/Structural/Logger/Log.js +10 -10
  14. package/Slice/Components/Structural/Logger/Logger.js +146 -146
  15. package/Slice/Components/Structural/Router/Router.js +721 -721
  16. package/Slice/Components/Structural/StylesManager/StylesManager.js +78 -78
  17. package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +84 -84
  18. package/Slice/Slice.js +542 -542
  19. package/Slice/tests/build-bundled-component-without-category.test.js +103 -103
  20. package/Slice/tests/build-js-only-visual-components.test.js +144 -144
  21. package/Slice/tests/bundle-v2-runtime-contract.test.js +728 -728
  22. package/Slice/tests/props-allowed-values-validation.test.js +119 -0
  23. package/Slice/tests/public-env-runtime-accessors.test.js +44 -44
  24. package/Slice/tests/router-loading-finally.test.js +68 -68
  25. package/api/index.js +286 -286
  26. package/api/middleware/securityMiddleware.js +252 -252
  27. package/api/tests/public-env-resolver.test.js +193 -193
  28. package/api/utils/publicEnvResolver.js +117 -117
  29. package/package.json +38 -37
  30. package/sliceConfig.schema.json +109 -109
  31. package/src/App/index.html +22 -22
  32. package/src/App/index.js +23 -23
  33. package/src/App/style.css +40 -40
  34. package/src/Components/AppComponents/HomePage/HomePage.css +201 -201
  35. package/src/Components/AppComponents/HomePage/HomePage.html +37 -37
  36. package/src/Components/AppComponents/HomePage/HomePage.js +210 -210
  37. package/src/Components/AppComponents/Playground/Playground.css +11 -11
  38. package/src/Components/AppComponents/Playground/Playground.js +111 -111
  39. package/src/Components/Service/FetchManager/FetchManager.js +133 -133
  40. package/src/Components/Service/IndexedDbManager/IndexedDbManager.js +141 -141
  41. package/src/Components/Service/LocalStorageManager/LocalStorageManager.js +45 -45
  42. package/src/Components/Visual/Button/Button.css +47 -47
  43. package/src/Components/Visual/Button/Button.html +5 -5
  44. package/src/Components/Visual/Button/Button.js +92 -92
  45. package/src/Components/Visual/Card/Card.css +68 -68
  46. package/src/Components/Visual/Card/Card.html +7 -7
  47. package/src/Components/Visual/Card/Card.js +107 -107
  48. package/src/Components/Visual/Checkbox/Checkbox.css +87 -87
  49. package/src/Components/Visual/Checkbox/Checkbox.html +8 -8
  50. package/src/Components/Visual/Checkbox/Checkbox.js +86 -86
  51. package/src/Components/Visual/CodeVisualizer/CodeVisualizer.css +129 -129
  52. package/src/Components/Visual/CodeVisualizer/CodeVisualizer.html +3 -3
  53. package/src/Components/Visual/CodeVisualizer/CodeVisualizer.js +262 -262
  54. package/src/Components/Visual/Details/Details.css +70 -70
  55. package/src/Components/Visual/Details/Details.html +9 -9
  56. package/src/Components/Visual/Details/Details.js +76 -76
  57. package/src/Components/Visual/DropDown/DropDown.css +60 -60
  58. package/src/Components/Visual/DropDown/DropDown.html +5 -5
  59. package/src/Components/Visual/DropDown/DropDown.js +63 -63
  60. package/src/Components/Visual/Grid/Grid.css +7 -7
  61. package/src/Components/Visual/Grid/Grid.html +1 -1
  62. package/src/Components/Visual/Grid/Grid.js +57 -57
  63. package/src/Components/Visual/Icon/Icon.css +510 -510
  64. package/src/Components/Visual/Icon/Icon.js +89 -89
  65. package/src/Components/Visual/Icon/slc.json +554 -554
  66. package/src/Components/Visual/Icon/slc.styl +507 -507
  67. package/src/Components/Visual/Icon/slc.svg +1485 -1485
  68. package/src/Components/Visual/Icon/slc.symbol.svg +1058 -1058
  69. package/src/Components/Visual/Input/Input.css +91 -91
  70. package/src/Components/Visual/Input/Input.html +4 -4
  71. package/src/Components/Visual/Input/Input.js +215 -215
  72. package/src/Components/Visual/Layout/Layout.js +49 -49
  73. package/src/Components/Visual/Link/Link.css +8 -8
  74. package/src/Components/Visual/Link/Link.html +1 -1
  75. package/src/Components/Visual/Link/Link.js +63 -63
  76. package/src/Components/Visual/Loading/Loading.css +56 -56
  77. package/src/Components/Visual/Loading/Loading.html +83 -83
  78. package/src/Components/Visual/Loading/Loading.js +38 -38
  79. package/src/Components/Visual/MultiRoute/MultiRoute.js +93 -93
  80. package/src/Components/Visual/Navbar/Navbar.css +115 -115
  81. package/src/Components/Visual/Navbar/Navbar.html +44 -44
  82. package/src/Components/Visual/Navbar/Navbar.js +141 -141
  83. package/src/Components/Visual/NotFound/NotFound.css +116 -116
  84. package/src/Components/Visual/NotFound/NotFound.html +23 -23
  85. package/src/Components/Visual/NotFound/NotFound.js +16 -16
  86. package/src/Components/Visual/Route/Route.js +93 -93
  87. package/src/Components/Visual/Select/Select.css +84 -84
  88. package/src/Components/Visual/Select/Select.html +8 -8
  89. package/src/Components/Visual/Select/Select.js +195 -195
  90. package/src/Components/Visual/Switch/Switch.css +76 -76
  91. package/src/Components/Visual/Switch/Switch.html +8 -8
  92. package/src/Components/Visual/Switch/Switch.js +102 -102
  93. package/src/Components/Visual/TreeItem/TreeItem.css +36 -36
  94. package/src/Components/Visual/TreeItem/TreeItem.html +1 -1
  95. package/src/Components/Visual/TreeItem/TreeItem.js +126 -126
  96. package/src/Components/Visual/TreeView/TreeView.css +8 -8
  97. package/src/Components/Visual/TreeView/TreeView.html +1 -1
  98. package/src/Components/Visual/TreeView/TreeView.js +48 -48
  99. package/src/Components/components.js +27 -27
  100. package/src/Styles/sliceStyles.css +34 -34
  101. package/src/Themes/Dark.css +42 -42
  102. package/src/Themes/Light.css +31 -31
  103. package/src/Themes/Slice.css +47 -47
  104. package/src/routes.js +15 -15
  105. package/src/sliceConfig.json +73 -73
  106. package/src/testing.js +887 -887
  107. package/types/index.d.ts +207 -0
@@ -1,1548 +1,1548 @@
1
- // ✅ VERSIÓN ANTI-INTERFERENCIA - Aislada del Router y con debugging
2
-
3
- /**
4
- * Runtime UI debugger for Slice components.
5
- */
6
- export default class Debugger extends HTMLElement {
7
- constructor() {
8
- super();
9
- this.toggleClick = slice.debuggerConfig.click;
10
- this.toggle = 'click';
11
- this.selectedComponentSliceId = null;
12
- this.isActive = false;
13
- this.activeTab = 'props';
14
- this.currentComponent = null;
15
- this.componentProps = {};
16
- this.currentEditingProp = null;
17
- this.currentEditingType = null;
18
-
19
- // ✅ Flag para prevenir interferencias externas
20
- this.isDebuggerInput = false;
21
- }
22
-
23
- /**
24
- * Load debugger UI and enable interactions.
25
- * @returns {Promise<boolean>}
26
- */
27
- async enableDebugMode() {
28
- //const html = await slice.controller.fetchText('Debugger', 'html', 'Structural');
29
- //const css = await slice.controller.fetchText('Debugger', 'css', 'Structural');
30
-
31
- const html = productionOnlyHtml();
32
- const css = productionOnlyCSS();
33
-
34
- this.innerHTML = html;
35
- slice.stylesManager.registerComponentStyles('Debugger', css);
36
-
37
- this.setupElements();
38
- this.setupEventListeners();
39
- this.makeDraggable();
40
-
41
- slice.logger.logInfo('Debugger', 'Advanced Debug mode enabled');
42
- return true;
43
- }
44
-
45
- /**
46
- * Cache UI elements.
47
- * @returns {void}
48
- */
49
- setupElements() {
50
- this.debuggerContainer = this.querySelector('#debugger-container');
51
- this.closeDebugger = this.querySelector('#close-debugger');
52
- this.propsContainer = this.querySelector('.props-container');
53
- this.infoContainer = this.querySelector('.info-list');
54
- this.editorModal = this.querySelector('#editor-modal');
55
- this.propertyEditor = this.querySelector('#property-editor');
56
- this.modalTitle = this.querySelector('#modal-title');
57
- this.validationMessage = this.querySelector('.validation-message');
58
-
59
- // Header elements
60
- this.componentName = this.querySelector('.component-name');
61
- this.componentId = this.querySelector('.component-id');
62
- }
63
-
64
- /**
65
- * Bind UI event listeners.
66
- * @returns {void}
67
- */
68
- setupEventListeners() {
69
- // Tab navigation
70
- this.querySelectorAll('.tab-btn').forEach(btn => {
71
- btn.addEventListener('click', (e) => {
72
- this.switchTab(e.target.dataset.tab);
73
- });
74
- });
75
-
76
- // Close and minimize
77
- this.closeDebugger.addEventListener('click', () => {
78
- this.hide();
79
- this.isActive = false;
80
- });
81
-
82
- // Modal events
83
- this.querySelector('#modal-close').addEventListener('click', () => {
84
- this.closeModal();
85
- });
86
-
87
- this.querySelector('#modal-cancel').addEventListener('click', () => {
88
- this.closeModal();
89
- });
90
-
91
- this.querySelector('#modal-save').addEventListener('click', () => {
92
- this.savePropertyValue();
93
- });
94
-
95
- // Editor type selector
96
- this.querySelectorAll('.type-btn').forEach(btn => {
97
- btn.addEventListener('click', (e) => {
98
- this.switchEditorType(e.target.dataset.type);
99
- });
100
- });
101
-
102
- // Action buttons
103
- this.querySelector('#apply-changes')?.addEventListener('click', (e) => {
104
- e.stopPropagation();
105
- this.applyAllChanges();
106
- });
107
-
108
- this.querySelector('#reset-values')?.addEventListener('click', (e) => {
109
- e.stopPropagation();
110
- this.resetValues();
111
- });
112
-
113
- // Property editor validation
114
- this.propertyEditor.addEventListener('input', () => {
115
- this.validateEditor();
116
- });
117
-
118
- // Modal backdrop click
119
- this.editorModal.addEventListener('click', (e) => {
120
- if (e.target === this.editorModal) {
121
- this.closeModal();
122
- }
123
- });
124
-
125
- // ✅ EVENTOS PRINCIPALES - Con protecciÃŗn anti-interferencia
126
- this.addEventListener('mousedown', (event) => {
127
- if (event.target.classList.contains('prop-control')) {
128
- this.isDebuggerInput = true;
129
- // Prevenir interferencias del Router u otros sistemas
130
- event.stopPropagation();
131
- }
132
- });
133
-
134
- this.addEventListener('focus', (event) => {
135
- if (event.target.classList.contains('prop-control')) {
136
- this.isDebuggerInput = true;
137
- event.stopPropagation();
138
- }
139
- }, true);
140
-
141
- this.addEventListener('blur', (event) => {
142
- if (event.target.classList.contains('prop-control')) {
143
- this.isDebuggerInput = false;
144
- }
145
- }, true);
146
-
147
- this.addEventListener('keypress', (event) => {
148
- if (event.key === 'Enter' && event.target.classList.contains('prop-control')) {
149
- event.preventDefault();
150
- event.stopPropagation();
151
- this.applyPropertyChange(event.target);
152
- }
153
- });
154
-
155
- this.addEventListener('change', (event) => {
156
- if (event.target.type === 'checkbox' && event.target.classList.contains('prop-control')) {
157
- event.stopPropagation();
158
- this.applyPropertyChange(event.target);
159
- }
160
- });
161
-
162
- // ✅ PROTECCIÓN GLOBAL: Prevenir que eventos externos interfieran
163
- this.addEventListener('click', (event) => {
164
- if (this.contains(event.target)) {
165
- event.stopPropagation();
166
- }
167
- });
168
-
169
- // ✅ Los eventos DOMNodeInserted/Removed estÃĄn deprecated,
170
- // pero la protecciÃŗn con stopPropagation() ya es suficiente
171
- }
172
-
173
- /**
174
- * Switch active tab.
175
- * @param {string} tabName
176
- * @returns {void}
177
- */
178
- switchTab(tabName) {
179
- this.activeTab = tabName;
180
-
181
- this.querySelectorAll('.tab-btn').forEach(btn => {
182
- btn.classList.toggle('active', btn.dataset.tab === tabName);
183
- });
184
-
185
- this.querySelectorAll('.tab-pane').forEach(pane => {
186
- pane.classList.toggle('active', pane.id === `${tabName}-tab`);
187
- });
188
- }
189
-
190
- /**
191
- * Switch editor type (json | function).
192
- * @param {string} type
193
- * @returns {void}
194
- */
195
- switchEditorType(type) {
196
- const normalized = this.normalizeEditorType(type);
197
- if (!normalized) {
198
- return;
199
- }
200
- this.querySelectorAll('.type-btn').forEach(btn => {
201
- btn.classList.toggle('active', btn.dataset.type === normalized);
202
- });
203
- this.currentEditingType = normalized;
204
- }
205
-
206
- /**
207
- * Normalize editor type to supported values.
208
- * @param {string} type
209
- * @returns {string|null}
210
- */
211
- normalizeEditorType(type) {
212
- if (type === 'json' || type === 'function') {
213
- return type;
214
- }
215
- return null;
216
- }
217
-
218
- /**
219
- * Attach debugger toggle handler to a component.
220
- * @param {HTMLElement} component
221
- * @returns {void}
222
- */
223
- attachDebugMode(component) {
224
- if (this.toggleClick === 'right') {
225
- this.toggle = 'contextmenu';
226
- } else {
227
- this.toggle = 'click';
228
- }
229
- component.addEventListener(this.toggle, (event) => this.handleDebugClick(event, component));
230
- }
231
-
232
- /**
233
- * Enable drag interaction for the debugger panel.
234
- * @returns {void}
235
- */
236
- makeDraggable() {
237
- let offset = { x: 0, y: 0 };
238
- let isDragging = false;
239
-
240
- const header = this.querySelector('.debugger-header');
241
-
242
- header.addEventListener('mousedown', (event) => {
243
- isDragging = true;
244
- offset.x = event.clientX - this.debuggerContainer.getBoundingClientRect().left;
245
- offset.y = event.clientY - this.debuggerContainer.getBoundingClientRect().top;
246
- header.style.cursor = 'grabbing';
247
- });
248
-
249
- document.addEventListener('mousemove', (event) => {
250
- if (isDragging) {
251
- const x = event.clientX - offset.x;
252
- const y = event.clientY - offset.y;
253
- this.debuggerContainer.style.left = `${x}px`;
254
- this.debuggerContainer.style.top = `${y}px`;
255
- this.debuggerContainer.style.right = 'auto';
256
- }
257
- });
258
-
259
- document.addEventListener('mouseup', () => {
260
- if (isDragging) {
261
- isDragging = false;
262
- header.style.cursor = 'grab';
263
- }
264
- });
265
- }
266
-
267
- /**
268
- * Handle toggle click and load component info.
269
- * @param {Event} event
270
- * @param {HTMLElement} component
271
- * @returns {void}
272
- */
273
- handleDebugClick(event, component) {
274
- event.preventDefault();
275
- event.stopPropagation();
276
-
277
- this.selectedComponentSliceId = component.sliceId;
278
- this.currentComponent = component;
279
- this.isActive = true;
280
-
281
- // Update header info
282
- this.componentName.textContent = component.constructor.name;
283
- this.componentId.textContent = `ID: ${component.sliceId}`;
284
-
285
- // Gather component data
286
- const realComponentProps = this.getComponentPropsForDebugger(component);
287
- this.componentProps = {};
288
-
289
- realComponentProps.forEach((attr) => {
290
- if (component[attr] === undefined) {
291
- this.componentProps[attr] = component[`_${attr}`];
292
- } else {
293
- this.componentProps[attr] = component[attr];
294
- }
295
- });
296
-
297
- // ✅ Crear UI sin interferencias
298
- this.updateDebuggerContent();
299
- this.debuggerContainer.classList.add('active');
300
- }
301
-
302
- updateDebuggerContent() {
303
- this.updatePropsTab();
304
- this.updateInfoTab();
305
- }
306
-
307
- /**
308
- * Render props tab content.
309
- * @returns {void}
310
- */
311
- updatePropsTab() {
312
- const propsContainer = this.querySelector('.props-container');
313
- if (!propsContainer) {
314
- return;
315
- }
316
-
317
- propsContainer.innerHTML = '';
318
-
319
- const realComponentProps = this.getComponentPropsForDebugger(this.currentComponent);
320
- const ComponentClass = this.currentComponent.constructor;
321
- const configuredProps = ComponentClass.props || {};
322
-
323
- realComponentProps.forEach(prop => {
324
- const propElement = this.createPropElement(prop, configuredProps[prop]);
325
- propsContainer.appendChild(propElement);
326
- });
327
- }
328
-
329
- /**
330
- * Build a prop row element.
331
- * @param {string} prop
332
- * @param {Object} [config]
333
- * @returns {HTMLElement}
334
- */
335
- createPropElement(prop, config = {}) {
336
- const propWrapper = document.createElement('div');
337
- propWrapper.className = 'prop-item';
338
- propWrapper.dataset.prop = prop;
339
-
340
- const currentValue = this.currentComponent[prop];
341
- const valueType = this.getValueType(currentValue);
342
-
343
- // Status based on usage
344
- let status, statusClass;
345
- if (currentValue !== undefined && currentValue !== null) {
346
- status = 'Used';
347
- statusClass = 'status-used';
348
- } else if (config.required) {
349
- status = 'Missing';
350
- statusClass = 'status-missing';
351
- } else {
352
- status = 'Optional';
353
- statusClass = 'status-optional';
354
- }
355
-
356
- propWrapper.innerHTML = `
357
- <div class="prop-header">
358
- <div class="prop-title">
359
- <strong>${prop}</strong>
360
- <span class="prop-type">${valueType}</span>
361
- </div>
362
- <div class="prop-status ${statusClass}">${status}</div>
363
- </div>
364
- <div class="prop-input">
365
- ${this.createInputForType(prop, currentValue, valueType, config)}
366
- </div>
367
- ${config.default !== undefined ? `<div class="default-value">Default: ${JSON.stringify(config.default)}</div>` : ''}
368
- `;
369
-
370
- return propWrapper;
371
- }
372
-
373
- /**
374
- * Build input HTML for a prop type.
375
- * @param {string} prop
376
- * @param {any} value
377
- * @param {string} type
378
- * @param {Object} [config]
379
- * @returns {string}
380
- */
381
- createInputForType(prop, value, type, config = {}) {
382
- const serializedValue = this.serializeValue(value);
383
-
384
- if (type === 'boolean') {
385
- return `
386
- <div class="input-group">
387
- <input type="checkbox"
388
- class="prop-control debugger-input"
389
- data-prop="${prop}"
390
- ${value ? 'checked' : ''}
391
- data-debugger-input="true">
392
- <span class="checkbox-label">${value ? 'true' : 'false'}</span>
393
- </div>
394
- `;
395
- } else if (type === 'number') {
396
- return `
397
- <div class="input-group">
398
- <input type="number"
399
- class="prop-control debugger-input"
400
- data-prop="${prop}"
401
- value="${serializedValue}"
402
- step="any"
403
- placeholder="Enter number..."
404
- data-debugger-input="true">
405
- </div>
406
- `;
407
- } else if (type === 'object' || type === 'array' || type === 'function') {
408
- return `
409
- <div class="input-group">
410
- <input type="text"
411
- class="prop-control debugger-input"
412
- data-prop="${prop}"
413
- value="${serializedValue}"
414
- readonly
415
- title="Click edit button to modify"
416
- data-debugger-input="true">
417
- <button class="edit-btn" onclick="slice.debugger.openAdvancedEditor('${prop}', '${type}')">âœī¸</button>
418
- </div>
419
- `;
420
- } else {
421
- return `
422
- <div class="input-group">
423
- <input type="text"
424
- class="prop-control debugger-input"
425
- data-prop="${prop}"
426
- value="${serializedValue}"
427
- placeholder="Enter value..."
428
- data-debugger-input="true">
429
- </div>
430
- `;
431
- }
432
- }
433
-
434
- /**
435
- * Apply a single prop change from an input.
436
- * @param {HTMLInputElement} inputElement
437
- * @returns {void}
438
- */
439
- applyPropertyChange(inputElement) {
440
- const prop = inputElement.dataset.prop;
441
- if (!prop) return;
442
-
443
- let newValue;
444
-
445
- if (inputElement.type === 'checkbox') {
446
- newValue = inputElement.checked;
447
- const label = inputElement.parentNode.querySelector('.checkbox-label');
448
- if (label) {
449
- label.textContent = newValue ? 'true' : 'false';
450
- }
451
- } else if (inputElement.type === 'number') {
452
- newValue = Number(inputElement.value);
453
- } else {
454
- newValue = inputElement.value;
455
-
456
- // Convert string values
457
- if (newValue === 'true') newValue = true;
458
- if (newValue === 'false') newValue = false;
459
- if (!isNaN(newValue) && newValue !== '' && newValue !== null) newValue = Number(newValue);
460
- }
461
-
462
- const oldValue = this.currentComponent[prop];
463
-
464
- this.currentComponent[prop] = newValue;
465
- slice.logger.logInfo('Debugger', `Updated ${prop}: ${oldValue} → ${newValue}`);
466
-
467
- this.showVisualFeedback(inputElement);
468
- }
469
-
470
- /**
471
- * Show temporary highlight on updated input.
472
- * @param {HTMLElement} inputElement
473
- * @returns {void}
474
- */
475
- showVisualFeedback(inputElement) {
476
- const originalBorder = inputElement.style.borderColor;
477
- const originalBoxShadow = inputElement.style.boxShadow;
478
-
479
- inputElement.style.borderColor = '#4CAF50';
480
- inputElement.style.boxShadow = '0 0 0 2px rgba(76, 175, 80, 0.3)';
481
-
482
- setTimeout(() => {
483
- inputElement.style.borderColor = originalBorder;
484
- inputElement.style.boxShadow = originalBoxShadow;
485
- }, 1500);
486
- }
487
-
488
- /**
489
- * Apply all editable prop changes in the panel.
490
- * @returns {void}
491
- */
492
- applyAllChanges() {
493
- const inputs = this.querySelectorAll('.prop-control:not([readonly])');
494
- let changeCount = 0;
495
-
496
- inputs.forEach(input => {
497
- if (!input.readOnly) {
498
- this.applyPropertyChange(input);
499
- changeCount++;
500
- }
501
- });
502
-
503
- slice.logger.logInfo('Debugger', `Applied ${changeCount} property changes`);
504
- this.showApplyFeedback(changeCount);
505
- }
506
-
507
- /**
508
- * Show apply feedback in button.
509
- * @param {number} changeCount
510
- * @returns {void}
511
- */
512
- showApplyFeedback(changeCount) {
513
- const applyBtn = this.querySelector('#apply-changes');
514
- if (!applyBtn) return;
515
-
516
- const originalText = applyBtn.textContent;
517
-
518
- if (changeCount > 0) {
519
- applyBtn.textContent = `✅ Applied ${changeCount} changes!`;
520
- applyBtn.style.background = '#4CAF50';
521
- } else {
522
- applyBtn.textContent = '✅ No changes to apply';
523
- applyBtn.style.background = '#9E9E9E';
524
- }
525
-
526
- setTimeout(() => {
527
- applyBtn.textContent = originalText;
528
- applyBtn.style.background = '';
529
- }, 2000);
530
- }
531
-
532
- /**
533
- * Open advanced editor for objects/functions.
534
- * @param {string} prop
535
- * @param {string} type
536
- * @returns {void}
537
- */
538
- openAdvancedEditor(prop, type) {
539
- this.currentEditingProp = prop;
540
- this.currentEditingType = this.normalizeEditorType(type) || 'json';
541
-
542
- const value = this.currentComponent[prop];
543
-
544
- this.modalTitle.textContent = `Edit ${prop} (${this.currentEditingType})`;
545
-
546
- this.querySelectorAll('.type-btn').forEach(btn => {
547
- btn.classList.toggle('active', btn.dataset.type === this.currentEditingType);
548
- });
549
-
550
- if (this.currentEditingType === 'function') {
551
- if (typeof value === 'function') {
552
- this.propertyEditor.value = value.toString();
553
- } else {
554
- this.propertyEditor.value = 'function() {\n // Your code here\n}';
555
- }
556
- } else {
557
- try {
558
- this.propertyEditor.value = JSON.stringify(value, null, 2);
559
- } catch (error) {
560
- this.propertyEditor.value = 'null';
561
- }
562
- }
563
-
564
- this.editorModal.classList.add('active');
565
- this.propertyEditor.focus();
566
- }
567
-
568
- /**
569
- * Validate editor contents for current type.
570
- * @returns {void}
571
- */
572
- validateEditor() {
573
- const value = this.propertyEditor.value.trim();
574
- const type = this.normalizeEditorType(this.currentEditingType) || 'json';
575
-
576
- try {
577
- if (type === 'function') {
578
- new Function('return ' + value)();
579
- } else {
580
- JSON.parse(value);
581
- }
582
-
583
- this.validationMessage.textContent = '✅ Valid syntax';
584
- this.validationMessage.style.color = '#4CAF50';
585
- this.querySelector('#modal-save').disabled = false;
586
- } catch (error) {
587
- this.validationMessage.textContent = `❌ ${error.message}`;
588
- this.validationMessage.style.color = '#F44336';
589
- this.querySelector('#modal-save').disabled = true;
590
- }
591
- }
592
-
593
- /**
594
- * Save editor value to component.
595
- * @returns {void}
596
- */
597
- savePropertyValue() {
598
- const value = this.propertyEditor.value.trim();
599
- const type = this.normalizeEditorType(this.currentEditingType) || 'json';
600
-
601
- try {
602
- let newValue;
603
-
604
- if (type === 'function') {
605
- newValue = new Function('return ' + value)();
606
- } else {
607
- newValue = JSON.parse(value);
608
- }
609
-
610
- this.currentComponent[this.currentEditingProp] = newValue;
611
- this.closeModal();
612
-
613
- slice.logger.logInfo('Debugger', `Updated ${this.currentEditingProp} via advanced editor`);
614
-
615
- const input = this.querySelector(`[data-prop="${this.currentEditingProp}"]`);
616
- if (input) {
617
- input.value = this.serializeValue(newValue);
618
- this.showVisualFeedback(input);
619
- }
620
-
621
- } catch (error) {
622
- this.validationMessage.textContent = `❌ ${error.message}`;
623
- this.validationMessage.style.color = '#F44336';
624
- }
625
- }
626
-
627
- /**
628
- * Close the advanced editor modal.
629
- * @returns {void}
630
- */
631
- closeModal() {
632
- this.editorModal.classList.remove('active');
633
- this.currentEditingProp = null;
634
- this.currentEditingType = null;
635
- this.validationMessage.textContent = '';
636
- }
637
-
638
- /**
639
- * Reset props to defaults (if defined in static props).
640
- * @returns {void}
641
- */
642
- resetValues() {
643
- const ComponentClass = this.currentComponent.constructor;
644
- const configuredProps = ComponentClass.props || {};
645
- let resetCount = 0;
646
-
647
- Object.entries(configuredProps).forEach(([prop, config]) => {
648
- if (config.default !== undefined) {
649
- this.currentComponent[prop] = config.default;
650
-
651
- const input = this.querySelector(`[data-prop="${prop}"]`);
652
- if (input && !input.readOnly) {
653
- if (input.type === 'checkbox') {
654
- input.checked = config.default;
655
- const label = input.parentNode.querySelector('.checkbox-label');
656
- if (label) {
657
- label.textContent = config.default ? 'true' : 'false';
658
- }
659
- } else {
660
- input.value = this.serializeValue(config.default);
661
- }
662
- resetCount++;
663
- }
664
- }
665
- });
666
-
667
- slice.logger.logInfo('Debugger', 'Reset values to defaults');
668
- this.showResetFeedback(resetCount);
669
- }
670
-
671
- /**
672
- * Show reset feedback in button.
673
- * @param {number} resetCount
674
- * @returns {void}
675
- */
676
- showResetFeedback(resetCount) {
677
- const resetBtn = this.querySelector('#reset-values');
678
- if (!resetBtn) return;
679
-
680
- const originalText = resetBtn.textContent;
681
-
682
- resetBtn.textContent = `🔄 Reset ${resetCount} values!`;
683
- resetBtn.style.background = '#FF9800';
684
-
685
- setTimeout(() => {
686
- resetBtn.textContent = originalText;
687
- resetBtn.style.background = '';
688
- }, 1500);
689
- }
690
-
691
- /**
692
- * Render info tab content.
693
- * @returns {void}
694
- */
695
- updateInfoTab() {
696
- const infoContainer = this.querySelector('.info-list');
697
- if (!infoContainer) return;
698
-
699
- const component = this.currentComponent;
700
-
701
- const info = [
702
- { label: 'Component Type', value: component.constructor.name },
703
- { label: 'Slice ID', value: component.sliceId || 'Not assigned' },
704
- { label: 'Tag Name', value: component.tagName },
705
- { label: 'Connected', value: component.isConnected ? 'Yes' : 'No' },
706
- { label: 'Props Count', value: Object.keys(this.componentProps).length },
707
- { label: 'Children', value: component.children.length }
708
- ];
709
-
710
- infoContainer.innerHTML = info.map(item => `
711
- <div class="info-item">
712
- <span class="info-label">${item.label}</span>
713
- <span class="info-value">${item.value}</span>
714
- </div>
715
- `).join('');
716
- }
717
-
718
- /**
719
- * Get a simple value type label.
720
- * @param {any} value
721
- * @returns {string}
722
- */
723
- getValueType(value) {
724
- if (value === null) return 'null';
725
- if (value === undefined) return 'undefined';
726
- if (Array.isArray(value)) return 'array';
727
- return typeof value;
728
- }
729
-
730
- /**
731
- * Serialize a value for input display.
732
- * @param {any} value
733
- * @returns {string}
734
- */
735
- serializeValue(value) {
736
- if (value === null || value === undefined) {
737
- return '';
738
- }
739
-
740
- if (typeof value === 'object' || typeof value === 'function') {
741
- try {
742
- return JSON.stringify(value);
743
- } catch {
744
- return String(value);
745
- }
746
- }
747
-
748
- return String(value);
749
- }
750
-
751
- /**
752
- * Resolve which props to show in the debugger.
753
- * @param {HTMLElement} component
754
- * @returns {string[]}
755
- */
756
- getComponentPropsForDebugger(component) {
757
- const ComponentClass = component.constructor;
758
-
759
- if (ComponentClass.props) {
760
- return Object.keys(ComponentClass.props);
761
- }
762
-
763
- if (component.debuggerProps && Array.isArray(component.debuggerProps)) {
764
- return component.debuggerProps;
765
- }
766
-
767
- return this.detectUsedProps(component);
768
- }
769
-
770
- /**
771
- * Detect props from backing fields on the component.
772
- * @param {HTMLElement} component
773
- * @returns {string[]}
774
- */
775
- detectUsedProps(component) {
776
- const usedProps = [];
777
-
778
- Object.getOwnPropertyNames(component).forEach(key => {
779
- if (key.startsWith('_') && key !== '_isActive') {
780
- const propName = key.substring(1);
781
- usedProps.push(propName);
782
- }
783
- });
784
-
785
- return usedProps;
786
- }
787
-
788
- /**
789
- * Hide the debugger UI.
790
- * @returns {void}
791
- */
792
- hide() {
793
- this.debuggerContainer.classList.remove('active');
794
- this.closeModal();
795
- }
796
- }
797
-
798
- customElements.define('slice-debugger', Debugger);
799
-
800
- function productionOnlyCSS(){
801
- return `
802
- #debugger-container {
803
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
804
- display: none;
805
- position: fixed;
806
- top: 20px;
807
- right: 20px;
808
- width: min(420px, calc(100vw - 40px));
809
- height: 85vh;
810
- max-height: 85vh;
811
- background: var(--primary-background-color);
812
- border: 1px solid var(--medium-color);
813
- border-radius: 12px;
814
- box-shadow: 0 20px 40px rgba(var(--primary-color-rgb), 0.15), 0 4px 12px rgba(0, 0, 0, 0.1);
815
- z-index: 10000;
816
- overflow: hidden;
817
- backdrop-filter: blur(10px);
818
- box-sizing: border-box;
819
- }
820
-
821
- #debugger-container.active {
822
- display: flex;
823
- flex-direction: column;
824
- }
825
-
826
- .debugger-header {
827
- background: linear-gradient(135deg, var(--primary-color), var(--primary-color-shade));
828
- color: var(--primary-color-contrast);
829
- padding: 12px 16px;
830
- border-radius: 12px 12px 0 0;
831
- user-select: none;
832
- cursor: grab;
833
- }
834
-
835
- .debugger-header:active {
836
- cursor: grabbing;
837
- }
838
-
839
- .header-content {
840
- display: flex;
841
- justify-content: space-between;
842
- align-items: center;
843
- }
844
-
845
- .component-info {
846
- display: flex;
847
- align-items: center;
848
- gap: 10px;
849
- }
850
-
851
- .component-icon {
852
- font-size: 20px;
853
- opacity: 0.9;
854
- }
855
-
856
- .component-name {
857
- font-size: 14px;
858
- font-weight: 600;
859
- margin-bottom: 2px;
860
- }
861
-
862
- .component-id {
863
- font-size: 11px;
864
- opacity: 0.8;
865
- }
866
-
867
- .header-actions {
868
- display: flex;
869
- gap: 8px;
870
- }
871
-
872
- .minimize-btn, #close-debugger {
873
- background: rgba(255, 255, 255, 0.2);
874
- border: none;
875
- color: var(--primary-color-contrast);
876
- width: 28px;
877
- height: 28px;
878
- border-radius: 6px;
879
- cursor: pointer;
880
- display: flex;
881
- align-items: center;
882
- justify-content: center;
883
- font-size: 16px;
884
- font-weight: bold;
885
- transition: background 0.2s ease;
886
- }
887
-
888
- .minimize-btn:hover, #close-debugger:hover {
889
- background: rgba(255, 255, 255, 0.3);
890
- }
891
-
892
- .debugger-content {
893
- flex: 1;
894
- display: flex;
895
- flex-direction: column;
896
- overflow: hidden;
897
- }
898
-
899
- .tabs-container {
900
- border-bottom: 1px solid var(--medium-color);
901
- }
902
-
903
- .tab-nav {
904
- display: flex;
905
- background: var(--tertiary-background-color);
906
- }
907
-
908
- .tab-btn {
909
- flex: 1;
910
- padding: 10px 14px;
911
- border: none;
912
- background: transparent;
913
- color: var(--font-secondary-color);
914
- font-size: 12px;
915
- font-weight: 500;
916
- cursor: pointer;
917
- transition: all 0.2s ease;
918
- border-bottom: 2px solid transparent;
919
- }
920
-
921
- .tab-btn:hover {
922
- background: var(--secondary-background-color);
923
- color: var(--font-primary-color);
924
- }
925
-
926
- .tab-btn.active {
927
- background: var(--primary-background-color);
928
- color: var(--primary-color);
929
- border-bottom-color: var(--primary-color);
930
- font-weight: 600;
931
- }
932
-
933
- .tab-content {
934
- flex: 1;
935
- overflow: hidden;
936
- min-height: 0;
937
- }
938
-
939
- .tab-pane {
940
- display: none;
941
- height: 100%;
942
- overflow-y: auto;
943
- overflow-x: hidden;
944
- padding: 16px;
945
- box-sizing: border-box;
946
- }
947
-
948
- .tab-pane.active {
949
- display: block;
950
- }
951
-
952
- .tab-pane::-webkit-scrollbar {
953
- width: 4px;
954
- }
955
-
956
- .tab-pane::-webkit-scrollbar-track {
957
- background: var(--tertiary-background-color);
958
- border-radius: 2px;
959
- }
960
-
961
- .tab-pane::-webkit-scrollbar-thumb {
962
- background: var(--medium-color);
963
- border-radius: 2px;
964
- }
965
-
966
- .tab-pane::-webkit-scrollbar-thumb:hover {
967
- background: var(--primary-color);
968
- }
969
-
970
- .props-container {
971
- display: flex;
972
- flex-direction: column;
973
- gap: 12px;
974
- margin-bottom: 16px;
975
- min-width: 0;
976
- overflow-x: hidden;
977
- }
978
-
979
- #debugger-container *,
980
- #debugger-container *::before,
981
- #debugger-container *::after {
982
- box-sizing: border-box;
983
- }
984
-
985
- .props-actions {
986
- border-top: 1px solid var(--medium-color);
987
- padding-top: 16px;
988
- margin-top: 8px;
989
- }
990
-
991
- .actions-note {
992
- margin-top: 12px;
993
- padding: 8px 12px;
994
- background: var(--tertiary-background-color);
995
- border-radius: 6px;
996
- border: 1px solid var(--medium-color);
997
- }
998
-
999
- .actions-note small {
1000
- color: var(--font-secondary-color);
1001
- font-size: 11px;
1002
- display: flex;
1003
- align-items: center;
1004
- gap: 6px;
1005
- }
1006
-
1007
- .props-section {
1008
- background: var(--tertiary-background-color);
1009
- border: 1px solid var(--medium-color);
1010
- border-radius: 8px;
1011
- padding: 12px;
1012
- }
1013
-
1014
- .section-title {
1015
- font-size: 12px;
1016
- font-weight: 600;
1017
- color: var(--font-primary-color);
1018
- margin-bottom: 12px;
1019
- display: flex;
1020
- align-items: center;
1021
- gap: 6px;
1022
- }
1023
-
1024
- .prop-item {
1025
- display: flex;
1026
- flex-direction: column;
1027
- gap: 6px;
1028
- padding: 12px;
1029
- background: var(--primary-background-color);
1030
- border: 1px solid var(--medium-color);
1031
- border-radius: 6px;
1032
- margin-bottom: 8px;
1033
- transition: border-color 0.2s ease;
1034
- min-width: 0;
1035
- overflow: hidden;
1036
- }
1037
-
1038
- .prop-item:hover {
1039
- border-color: var(--primary-color);
1040
- }
1041
-
1042
- .prop-header {
1043
- display: flex;
1044
- justify-content: space-between;
1045
- align-items: center;
1046
- gap: 8px;
1047
- min-width: 0;
1048
- }
1049
-
1050
- .prop-title {
1051
- display: flex;
1052
- align-items: center;
1053
- gap: 6px;
1054
- min-width: 0;
1055
- flex: 1;
1056
- }
1057
-
1058
- .prop-title strong {
1059
- min-width: 0;
1060
- overflow: hidden;
1061
- text-overflow: ellipsis;
1062
- white-space: nowrap;
1063
- }
1064
-
1065
- .prop-name {
1066
- font-size: 13px;
1067
- font-weight: 600;
1068
- color: var(--font-primary-color);
1069
- }
1070
-
1071
- .prop-name.required::after {
1072
- content: " *";
1073
- color: var(--danger-color);
1074
- }
1075
-
1076
- .prop-meta {
1077
- display: flex;
1078
- align-items: center;
1079
- gap: 8px;
1080
- }
1081
-
1082
- .prop-type {
1083
- font-size: 11px;
1084
- padding: 2px 6px;
1085
- background: var(--secondary-color);
1086
- color: var(--secondary-color-contrast);
1087
- border-radius: 4px;
1088
- font-family: monospace;
1089
- font-weight: 500;
1090
- flex-shrink: 0;
1091
- }
1092
-
1093
- .prop-status {
1094
- font-size: 12px;
1095
- font-weight: 500;
1096
- flex-shrink: 0;
1097
- }
1098
-
1099
- .status-used {
1100
- color: var(--success-color);
1101
- }
1102
-
1103
- .status-missing {
1104
- color: var(--danger-color);
1105
- }
1106
-
1107
- .status-optional {
1108
- color: var(--medium-color);
1109
- }
1110
-
1111
- .prop-input {
1112
- margin-top: 8px;
1113
- min-width: 0;
1114
- overflow: hidden;
1115
- }
1116
-
1117
- .input-group {
1118
- position: relative;
1119
- max-width: 100%;
1120
- min-width: 0;
1121
- overflow: hidden;
1122
- }
1123
-
1124
- .prop-control {
1125
- width: 100%;
1126
- max-width: 100%;
1127
- min-width: 0;
1128
- padding: 8px 32px 8px 10px;
1129
- border: 1px solid var(--medium-color);
1130
- border-radius: 6px;
1131
- background: var(--primary-background-color);
1132
- color: var(--font-primary-color);
1133
- font-size: 13px;
1134
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
1135
- font-family: monospace;
1136
- box-sizing: border-box;
1137
- display: block;
1138
- }
1139
-
1140
- .prop-control:focus {
1141
- outline: none;
1142
- border-color: var(--primary-color);
1143
- box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.1);
1144
- }
1145
-
1146
- .prop-control:disabled {
1147
- background: var(--disabled-color);
1148
- color: var(--font-secondary-color);
1149
- cursor: not-allowed;
1150
- }
1151
-
1152
- .prop-control[readonly] {
1153
- background: var(--tertiary-background-color);
1154
- cursor: pointer;
1155
- text-overflow: ellipsis;
1156
- overflow: hidden;
1157
- white-space: nowrap;
1158
- max-width: 100%;
1159
- }
1160
-
1161
- .prop-control[readonly]:focus {
1162
- border-color: var(--accent-color);
1163
- box-shadow: 0 0 0 3px rgba(var(--accent-color), 0.1);
1164
- }
1165
-
1166
- /* Estilos específicos para checkboxes */
1167
- .prop-control[type="checkbox"] {
1168
- width: auto;
1169
- padding: 0;
1170
- margin: 0;
1171
- cursor: pointer;
1172
- }
1173
-
1174
- .edit-btn {
1175
- position: absolute;
1176
- right: 4px;
1177
- top: 50%;
1178
- transform: translateY(-50%);
1179
- background: var(--accent-color);
1180
- border: none;
1181
- color: white;
1182
- width: 24px;
1183
- height: 24px;
1184
- border-radius: 4px;
1185
- cursor: pointer;
1186
- font-size: 12px;
1187
- display: flex;
1188
- align-items: center;
1189
- justify-content: center;
1190
- transition: background 0.2s ease;
1191
- }
1192
-
1193
- .edit-btn:hover {
1194
- background: var(--primary-color);
1195
- }
1196
-
1197
- .default-value {
1198
- font-size: 11px;
1199
- color: var(--font-secondary-color);
1200
- font-style: italic;
1201
- margin-top: 4px;
1202
- }
1203
-
1204
- .info-list {
1205
- display: flex;
1206
- flex-direction: column;
1207
- gap: 12px;
1208
- }
1209
-
1210
- .info-item {
1211
- display: flex;
1212
- justify-content: space-between;
1213
- padding: 12px;
1214
- background: var(--tertiary-background-color);
1215
- border-radius: 6px;
1216
- border: 1px solid var(--medium-color);
1217
- }
1218
-
1219
- .info-label {
1220
- font-weight: 600;
1221
- color: var(--font-primary-color);
1222
- font-size: 13px;
1223
- }
1224
-
1225
- .info-value {
1226
- color: var(--font-secondary-color);
1227
- font-family: monospace;
1228
- font-size: 12px;
1229
- }
1230
-
1231
- .actions-container {
1232
- display: flex;
1233
- flex-direction: column;
1234
- gap: 16px;
1235
- }
1236
-
1237
- .action-buttons {
1238
- display: flex;
1239
- flex-direction: column;
1240
- gap: 8px;
1241
- }
1242
-
1243
- .action-btn {
1244
- padding: 12px 16px;
1245
- border: none;
1246
- border-radius: 6px;
1247
- font-size: 13px;
1248
- font-weight: 500;
1249
- cursor: pointer;
1250
- transition: all 0.2s ease;
1251
- display: flex;
1252
- align-items: center;
1253
- justify-content: center;
1254
- gap: 8px;
1255
- }
1256
-
1257
- .action-btn.primary {
1258
- background: var(--primary-color);
1259
- color: var(--primary-color-contrast);
1260
- }
1261
-
1262
- .action-btn.primary:hover {
1263
- background: var(--primary-color-shade);
1264
- }
1265
-
1266
- .action-btn.secondary {
1267
- background: var(--secondary-color);
1268
- color: var(--secondary-color-contrast);
1269
- }
1270
-
1271
- .action-btn.secondary:hover {
1272
- opacity: 0.9;
1273
- }
1274
-
1275
- .action-btn.tertiary {
1276
- background: var(--tertiary-background-color);
1277
- color: var(--font-primary-color);
1278
- border: 1px solid var(--medium-color);
1279
- }
1280
-
1281
- .action-btn.tertiary:hover {
1282
- background: var(--secondary-background-color);
1283
- }
1284
-
1285
- /* Modal Styles */
1286
- .editor-modal {
1287
- display: none;
1288
- position: fixed;
1289
- top: 0;
1290
- left: 0;
1291
- width: 100%;
1292
- height: 100%;
1293
- background: rgba(0, 0, 0, 0.6);
1294
- z-index: 20000;
1295
- backdrop-filter: blur(4px);
1296
- }
1297
-
1298
- .editor-modal.active {
1299
- display: flex;
1300
- align-items: center;
1301
- justify-content: center;
1302
- }
1303
-
1304
- .modal-content {
1305
- background: var(--primary-background-color);
1306
- border-radius: 12px;
1307
- width: min(600px, 92vw);
1308
- max-width: 600px;
1309
- max-height: 80%;
1310
- display: flex;
1311
- flex-direction: column;
1312
- box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
1313
- border: 1px solid var(--medium-color);
1314
- box-sizing: border-box;
1315
- }
1316
-
1317
- .modal-header {
1318
- padding: 16px 20px;
1319
- background: var(--tertiary-background-color);
1320
- border-radius: 12px 12px 0 0;
1321
- border-bottom: 1px solid var(--medium-color);
1322
- display: flex;
1323
- justify-content: space-between;
1324
- align-items: center;
1325
- }
1326
-
1327
- .modal-header h3 {
1328
- margin: 0;
1329
- font-size: 16px;
1330
- font-weight: 600;
1331
- color: var(--font-primary-color);
1332
- }
1333
-
1334
- .modal-close {
1335
- background: none;
1336
- border: none;
1337
- font-size: 20px;
1338
- color: var(--font-secondary-color);
1339
- cursor: pointer;
1340
- width: 32px;
1341
- height: 32px;
1342
- border-radius: 6px;
1343
- display: flex;
1344
- align-items: center;
1345
- justify-content: center;
1346
- transition: background 0.2s ease;
1347
- }
1348
-
1349
- .modal-close:hover {
1350
- background: var(--secondary-background-color);
1351
- }
1352
-
1353
- .modal-body {
1354
- flex: 1;
1355
- padding: 20px;
1356
- display: flex;
1357
- flex-direction: column;
1358
- gap: 16px;
1359
- overflow: hidden;
1360
- min-height: 0;
1361
- }
1362
-
1363
- .editor-type-selector {
1364
- display: flex;
1365
- gap: 4px;
1366
- background: var(--tertiary-background-color);
1367
- padding: 4px;
1368
- border-radius: 6px;
1369
- }
1370
-
1371
- .type-btn {
1372
- flex: 1;
1373
- padding: 8px 12px;
1374
- border: none;
1375
- background: transparent;
1376
- color: var(--font-secondary-color);
1377
- font-size: 12px;
1378
- font-weight: 500;
1379
- cursor: pointer;
1380
- border-radius: 4px;
1381
- transition: all 0.2s ease;
1382
- }
1383
-
1384
- .type-btn.active {
1385
- background: var(--primary-color);
1386
- color: var(--primary-color-contrast);
1387
- }
1388
-
1389
- .editor-container {
1390
- flex: 1;
1391
- position: relative;
1392
- min-height: 200px;
1393
- min-width: 0;
1394
- }
1395
-
1396
- #property-editor {
1397
- width: 100%;
1398
- height: 100%;
1399
- border: 1px solid var(--medium-color);
1400
- border-radius: 6px;
1401
- padding: 12px;
1402
- background: var(--primary-background-color);
1403
- color: var(--font-primary-color);
1404
- font-family: 'Monaco', 'Consolas', monospace;
1405
- font-size: 13px;
1406
- line-height: 1.5;
1407
- resize: none;
1408
- outline: none;
1409
- min-height: 200px;
1410
- box-sizing: border-box;
1411
- }
1412
-
1413
- #property-editor:focus {
1414
- border-color: var(--primary-color);
1415
- box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.1);
1416
- }
1417
-
1418
- .validation-message {
1419
- font-size: 12px;
1420
- color: var(--danger-color);
1421
- min-height: 18px;
1422
- display: flex;
1423
- align-items: center;
1424
- gap: 6px;
1425
- }
1426
-
1427
- .modal-actions {
1428
- padding: 16px 20px;
1429
- background: var(--tertiary-background-color);
1430
- border-radius: 0 0 12px 12px;
1431
- border-top: 1px solid var(--medium-color);
1432
- display: flex;
1433
- gap: 12px;
1434
- justify-content: flex-end;
1435
- }
1436
-
1437
- .modal-btn {
1438
- padding: 10px 20px;
1439
- border: none;
1440
- border-radius: 6px;
1441
- font-size: 13px;
1442
- font-weight: 500;
1443
- cursor: pointer;
1444
- transition: all 0.2s ease;
1445
- }
1446
-
1447
- .modal-btn.cancel {
1448
- background: var(--tertiary-background-color);
1449
- color: var(--font-primary-color);
1450
- border: 1px solid var(--medium-color);
1451
- }
1452
-
1453
- .modal-btn.cancel:hover {
1454
- background: var(--secondary-background-color);
1455
- }
1456
-
1457
- .modal-btn.save {
1458
- background: var(--success-color);
1459
- color: var(--success-contrast);
1460
- }
1461
-
1462
- .modal-btn.save:hover {
1463
- opacity: 0.9;
1464
- }
1465
-
1466
- .modal-btn.save:disabled {
1467
- background: var(--disabled-color);
1468
- cursor: not-allowed;
1469
- }
1470
- `
1471
- }
1472
-
1473
- function productionOnlyHtml(){
1474
- return `
1475
- <div id="debugger-container">
1476
- <div class="debugger-header">
1477
- <div class="header-content">
1478
- <div class="component-info">
1479
- <div class="component-icon">🔍</div>
1480
- <div class="component-details">
1481
- <div class="component-name">Component Inspector</div>
1482
- <div class="component-id">Ready to debug</div>
1483
- </div>
1484
- </div>
1485
- <div class="header-actions">
1486
- <button class="minimize-btn" title="Minimize">−</button>
1487
- <button id="close-debugger" title="Close">×</button>
1488
- </div>
1489
- </div>
1490
- </div>
1491
-
1492
- <div class="debugger-content">
1493
- <div class="tabs-container">
1494
- <div class="tab-nav">
1495
- <button class="tab-btn active" data-tab="props">📋 Props</button>
1496
- <button class="tab-btn" data-tab="info">â„šī¸ Info</button>
1497
- </div>
1498
- </div>
1499
-
1500
- <div class="tab-content">
1501
- <div class="tab-pane active" id="props-tab">
1502
- <div class="props-container"></div>
1503
- <div class="props-actions">
1504
- <div class="action-buttons">
1505
- <button class="action-btn primary" id="apply-changes">✅ Apply Changes</button>
1506
- <button class="action-btn secondary" id="reset-values">🔄 Reset Values</button>
1507
- </div>
1508
- <div class="actions-note">
1509
- <small>💡 Press Enter on any input to apply changes automatically</small>
1510
- </div>
1511
- </div>
1512
- </div>
1513
-
1514
- <div class="tab-pane" id="info-tab">
1515
- <div class="info-container">
1516
- <div class="info-list"></div>
1517
- </div>
1518
- </div>
1519
- </div>
1520
- </div>
1521
-
1522
- <!-- Modal para editar objetos/funciones -->
1523
- <div class="editor-modal" id="editor-modal">
1524
- <div class="modal-content">
1525
- <div class="modal-header">
1526
- <h3 id="modal-title">Edit Property</h3>
1527
- <button class="modal-close" id="modal-close">×</button>
1528
- </div>
1529
- <div class="modal-body">
1530
- <div class="editor-type-selector">
1531
- <button class="type-btn active" data-type="json">📋 JSON</button>
1532
- <button class="type-btn" data-type="function">⚡ Function</button>
1533
- </div>
1534
- <div class="editor-container">
1535
- <textarea id="property-editor" spellcheck="false"></textarea>
1536
- </div>
1537
- <div class="editor-footer">
1538
- <div class="validation-message"></div>
1539
- </div>
1540
- </div>
1541
- <div class="modal-actions">
1542
- <button class="modal-btn cancel" id="modal-cancel">Cancel</button>
1543
- <button class="modal-btn save" id="modal-save">Save Changes</button>
1544
- </div>
1545
- </div>
1546
- </div>
1547
- </div>`
1
+ // ✅ VERSIÓN ANTI-INTERFERENCIA - Aislada del Router y con debugging
2
+
3
+ /**
4
+ * Runtime UI debugger for Slice components.
5
+ */
6
+ export default class Debugger extends HTMLElement {
7
+ constructor() {
8
+ super();
9
+ this.toggleClick = slice.debuggerConfig.click;
10
+ this.toggle = 'click';
11
+ this.selectedComponentSliceId = null;
12
+ this.isActive = false;
13
+ this.activeTab = 'props';
14
+ this.currentComponent = null;
15
+ this.componentProps = {};
16
+ this.currentEditingProp = null;
17
+ this.currentEditingType = null;
18
+
19
+ // ✅ Flag para prevenir interferencias externas
20
+ this.isDebuggerInput = false;
21
+ }
22
+
23
+ /**
24
+ * Load debugger UI and enable interactions.
25
+ * @returns {Promise<boolean>}
26
+ */
27
+ async enableDebugMode() {
28
+ //const html = await slice.controller.fetchText('Debugger', 'html', 'Structural');
29
+ //const css = await slice.controller.fetchText('Debugger', 'css', 'Structural');
30
+
31
+ const html = productionOnlyHtml();
32
+ const css = productionOnlyCSS();
33
+
34
+ this.innerHTML = html;
35
+ slice.stylesManager.registerComponentStyles('Debugger', css);
36
+
37
+ this.setupElements();
38
+ this.setupEventListeners();
39
+ this.makeDraggable();
40
+
41
+ slice.logger.logInfo('Debugger', 'Advanced Debug mode enabled');
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * Cache UI elements.
47
+ * @returns {void}
48
+ */
49
+ setupElements() {
50
+ this.debuggerContainer = this.querySelector('#debugger-container');
51
+ this.closeDebugger = this.querySelector('#close-debugger');
52
+ this.propsContainer = this.querySelector('.props-container');
53
+ this.infoContainer = this.querySelector('.info-list');
54
+ this.editorModal = this.querySelector('#editor-modal');
55
+ this.propertyEditor = this.querySelector('#property-editor');
56
+ this.modalTitle = this.querySelector('#modal-title');
57
+ this.validationMessage = this.querySelector('.validation-message');
58
+
59
+ // Header elements
60
+ this.componentName = this.querySelector('.component-name');
61
+ this.componentId = this.querySelector('.component-id');
62
+ }
63
+
64
+ /**
65
+ * Bind UI event listeners.
66
+ * @returns {void}
67
+ */
68
+ setupEventListeners() {
69
+ // Tab navigation
70
+ this.querySelectorAll('.tab-btn').forEach(btn => {
71
+ btn.addEventListener('click', (e) => {
72
+ this.switchTab(e.target.dataset.tab);
73
+ });
74
+ });
75
+
76
+ // Close and minimize
77
+ this.closeDebugger.addEventListener('click', () => {
78
+ this.hide();
79
+ this.isActive = false;
80
+ });
81
+
82
+ // Modal events
83
+ this.querySelector('#modal-close').addEventListener('click', () => {
84
+ this.closeModal();
85
+ });
86
+
87
+ this.querySelector('#modal-cancel').addEventListener('click', () => {
88
+ this.closeModal();
89
+ });
90
+
91
+ this.querySelector('#modal-save').addEventListener('click', () => {
92
+ this.savePropertyValue();
93
+ });
94
+
95
+ // Editor type selector
96
+ this.querySelectorAll('.type-btn').forEach(btn => {
97
+ btn.addEventListener('click', (e) => {
98
+ this.switchEditorType(e.target.dataset.type);
99
+ });
100
+ });
101
+
102
+ // Action buttons
103
+ this.querySelector('#apply-changes')?.addEventListener('click', (e) => {
104
+ e.stopPropagation();
105
+ this.applyAllChanges();
106
+ });
107
+
108
+ this.querySelector('#reset-values')?.addEventListener('click', (e) => {
109
+ e.stopPropagation();
110
+ this.resetValues();
111
+ });
112
+
113
+ // Property editor validation
114
+ this.propertyEditor.addEventListener('input', () => {
115
+ this.validateEditor();
116
+ });
117
+
118
+ // Modal backdrop click
119
+ this.editorModal.addEventListener('click', (e) => {
120
+ if (e.target === this.editorModal) {
121
+ this.closeModal();
122
+ }
123
+ });
124
+
125
+ // ✅ EVENTOS PRINCIPALES - Con protecciÃŗn anti-interferencia
126
+ this.addEventListener('mousedown', (event) => {
127
+ if (event.target.classList.contains('prop-control')) {
128
+ this.isDebuggerInput = true;
129
+ // Prevenir interferencias del Router u otros sistemas
130
+ event.stopPropagation();
131
+ }
132
+ });
133
+
134
+ this.addEventListener('focus', (event) => {
135
+ if (event.target.classList.contains('prop-control')) {
136
+ this.isDebuggerInput = true;
137
+ event.stopPropagation();
138
+ }
139
+ }, true);
140
+
141
+ this.addEventListener('blur', (event) => {
142
+ if (event.target.classList.contains('prop-control')) {
143
+ this.isDebuggerInput = false;
144
+ }
145
+ }, true);
146
+
147
+ this.addEventListener('keypress', (event) => {
148
+ if (event.key === 'Enter' && event.target.classList.contains('prop-control')) {
149
+ event.preventDefault();
150
+ event.stopPropagation();
151
+ this.applyPropertyChange(event.target);
152
+ }
153
+ });
154
+
155
+ this.addEventListener('change', (event) => {
156
+ if (event.target.type === 'checkbox' && event.target.classList.contains('prop-control')) {
157
+ event.stopPropagation();
158
+ this.applyPropertyChange(event.target);
159
+ }
160
+ });
161
+
162
+ // ✅ PROTECCIÓN GLOBAL: Prevenir que eventos externos interfieran
163
+ this.addEventListener('click', (event) => {
164
+ if (this.contains(event.target)) {
165
+ event.stopPropagation();
166
+ }
167
+ });
168
+
169
+ // ✅ Los eventos DOMNodeInserted/Removed estÃĄn deprecated,
170
+ // pero la protecciÃŗn con stopPropagation() ya es suficiente
171
+ }
172
+
173
+ /**
174
+ * Switch active tab.
175
+ * @param {string} tabName
176
+ * @returns {void}
177
+ */
178
+ switchTab(tabName) {
179
+ this.activeTab = tabName;
180
+
181
+ this.querySelectorAll('.tab-btn').forEach(btn => {
182
+ btn.classList.toggle('active', btn.dataset.tab === tabName);
183
+ });
184
+
185
+ this.querySelectorAll('.tab-pane').forEach(pane => {
186
+ pane.classList.toggle('active', pane.id === `${tabName}-tab`);
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Switch editor type (json | function).
192
+ * @param {string} type
193
+ * @returns {void}
194
+ */
195
+ switchEditorType(type) {
196
+ const normalized = this.normalizeEditorType(type);
197
+ if (!normalized) {
198
+ return;
199
+ }
200
+ this.querySelectorAll('.type-btn').forEach(btn => {
201
+ btn.classList.toggle('active', btn.dataset.type === normalized);
202
+ });
203
+ this.currentEditingType = normalized;
204
+ }
205
+
206
+ /**
207
+ * Normalize editor type to supported values.
208
+ * @param {string} type
209
+ * @returns {string|null}
210
+ */
211
+ normalizeEditorType(type) {
212
+ if (type === 'json' || type === 'function') {
213
+ return type;
214
+ }
215
+ return null;
216
+ }
217
+
218
+ /**
219
+ * Attach debugger toggle handler to a component.
220
+ * @param {HTMLElement} component
221
+ * @returns {void}
222
+ */
223
+ attachDebugMode(component) {
224
+ if (this.toggleClick === 'right') {
225
+ this.toggle = 'contextmenu';
226
+ } else {
227
+ this.toggle = 'click';
228
+ }
229
+ component.addEventListener(this.toggle, (event) => this.handleDebugClick(event, component));
230
+ }
231
+
232
+ /**
233
+ * Enable drag interaction for the debugger panel.
234
+ * @returns {void}
235
+ */
236
+ makeDraggable() {
237
+ let offset = { x: 0, y: 0 };
238
+ let isDragging = false;
239
+
240
+ const header = this.querySelector('.debugger-header');
241
+
242
+ header.addEventListener('mousedown', (event) => {
243
+ isDragging = true;
244
+ offset.x = event.clientX - this.debuggerContainer.getBoundingClientRect().left;
245
+ offset.y = event.clientY - this.debuggerContainer.getBoundingClientRect().top;
246
+ header.style.cursor = 'grabbing';
247
+ });
248
+
249
+ document.addEventListener('mousemove', (event) => {
250
+ if (isDragging) {
251
+ const x = event.clientX - offset.x;
252
+ const y = event.clientY - offset.y;
253
+ this.debuggerContainer.style.left = `${x}px`;
254
+ this.debuggerContainer.style.top = `${y}px`;
255
+ this.debuggerContainer.style.right = 'auto';
256
+ }
257
+ });
258
+
259
+ document.addEventListener('mouseup', () => {
260
+ if (isDragging) {
261
+ isDragging = false;
262
+ header.style.cursor = 'grab';
263
+ }
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Handle toggle click and load component info.
269
+ * @param {Event} event
270
+ * @param {HTMLElement} component
271
+ * @returns {void}
272
+ */
273
+ handleDebugClick(event, component) {
274
+ event.preventDefault();
275
+ event.stopPropagation();
276
+
277
+ this.selectedComponentSliceId = component.sliceId;
278
+ this.currentComponent = component;
279
+ this.isActive = true;
280
+
281
+ // Update header info
282
+ this.componentName.textContent = component.constructor.name;
283
+ this.componentId.textContent = `ID: ${component.sliceId}`;
284
+
285
+ // Gather component data
286
+ const realComponentProps = this.getComponentPropsForDebugger(component);
287
+ this.componentProps = {};
288
+
289
+ realComponentProps.forEach((attr) => {
290
+ if (component[attr] === undefined) {
291
+ this.componentProps[attr] = component[`_${attr}`];
292
+ } else {
293
+ this.componentProps[attr] = component[attr];
294
+ }
295
+ });
296
+
297
+ // ✅ Crear UI sin interferencias
298
+ this.updateDebuggerContent();
299
+ this.debuggerContainer.classList.add('active');
300
+ }
301
+
302
+ updateDebuggerContent() {
303
+ this.updatePropsTab();
304
+ this.updateInfoTab();
305
+ }
306
+
307
+ /**
308
+ * Render props tab content.
309
+ * @returns {void}
310
+ */
311
+ updatePropsTab() {
312
+ const propsContainer = this.querySelector('.props-container');
313
+ if (!propsContainer) {
314
+ return;
315
+ }
316
+
317
+ propsContainer.innerHTML = '';
318
+
319
+ const realComponentProps = this.getComponentPropsForDebugger(this.currentComponent);
320
+ const ComponentClass = this.currentComponent.constructor;
321
+ const configuredProps = ComponentClass.props || {};
322
+
323
+ realComponentProps.forEach(prop => {
324
+ const propElement = this.createPropElement(prop, configuredProps[prop]);
325
+ propsContainer.appendChild(propElement);
326
+ });
327
+ }
328
+
329
+ /**
330
+ * Build a prop row element.
331
+ * @param {string} prop
332
+ * @param {Object} [config]
333
+ * @returns {HTMLElement}
334
+ */
335
+ createPropElement(prop, config = {}) {
336
+ const propWrapper = document.createElement('div');
337
+ propWrapper.className = 'prop-item';
338
+ propWrapper.dataset.prop = prop;
339
+
340
+ const currentValue = this.currentComponent[prop];
341
+ const valueType = this.getValueType(currentValue);
342
+
343
+ // Status based on usage
344
+ let status, statusClass;
345
+ if (currentValue !== undefined && currentValue !== null) {
346
+ status = 'Used';
347
+ statusClass = 'status-used';
348
+ } else if (config.required) {
349
+ status = 'Missing';
350
+ statusClass = 'status-missing';
351
+ } else {
352
+ status = 'Optional';
353
+ statusClass = 'status-optional';
354
+ }
355
+
356
+ propWrapper.innerHTML = `
357
+ <div class="prop-header">
358
+ <div class="prop-title">
359
+ <strong>${prop}</strong>
360
+ <span class="prop-type">${valueType}</span>
361
+ </div>
362
+ <div class="prop-status ${statusClass}">${status}</div>
363
+ </div>
364
+ <div class="prop-input">
365
+ ${this.createInputForType(prop, currentValue, valueType, config)}
366
+ </div>
367
+ ${config.default !== undefined ? `<div class="default-value">Default: ${JSON.stringify(config.default)}</div>` : ''}
368
+ `;
369
+
370
+ return propWrapper;
371
+ }
372
+
373
+ /**
374
+ * Build input HTML for a prop type.
375
+ * @param {string} prop
376
+ * @param {any} value
377
+ * @param {string} type
378
+ * @param {Object} [config]
379
+ * @returns {string}
380
+ */
381
+ createInputForType(prop, value, type, config = {}) {
382
+ const serializedValue = this.serializeValue(value);
383
+
384
+ if (type === 'boolean') {
385
+ return `
386
+ <div class="input-group">
387
+ <input type="checkbox"
388
+ class="prop-control debugger-input"
389
+ data-prop="${prop}"
390
+ ${value ? 'checked' : ''}
391
+ data-debugger-input="true">
392
+ <span class="checkbox-label">${value ? 'true' : 'false'}</span>
393
+ </div>
394
+ `;
395
+ } else if (type === 'number') {
396
+ return `
397
+ <div class="input-group">
398
+ <input type="number"
399
+ class="prop-control debugger-input"
400
+ data-prop="${prop}"
401
+ value="${serializedValue}"
402
+ step="any"
403
+ placeholder="Enter number..."
404
+ data-debugger-input="true">
405
+ </div>
406
+ `;
407
+ } else if (type === 'object' || type === 'array' || type === 'function') {
408
+ return `
409
+ <div class="input-group">
410
+ <input type="text"
411
+ class="prop-control debugger-input"
412
+ data-prop="${prop}"
413
+ value="${serializedValue}"
414
+ readonly
415
+ title="Click edit button to modify"
416
+ data-debugger-input="true">
417
+ <button class="edit-btn" onclick="slice.debugger.openAdvancedEditor('${prop}', '${type}')">âœī¸</button>
418
+ </div>
419
+ `;
420
+ } else {
421
+ return `
422
+ <div class="input-group">
423
+ <input type="text"
424
+ class="prop-control debugger-input"
425
+ data-prop="${prop}"
426
+ value="${serializedValue}"
427
+ placeholder="Enter value..."
428
+ data-debugger-input="true">
429
+ </div>
430
+ `;
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Apply a single prop change from an input.
436
+ * @param {HTMLInputElement} inputElement
437
+ * @returns {void}
438
+ */
439
+ applyPropertyChange(inputElement) {
440
+ const prop = inputElement.dataset.prop;
441
+ if (!prop) return;
442
+
443
+ let newValue;
444
+
445
+ if (inputElement.type === 'checkbox') {
446
+ newValue = inputElement.checked;
447
+ const label = inputElement.parentNode.querySelector('.checkbox-label');
448
+ if (label) {
449
+ label.textContent = newValue ? 'true' : 'false';
450
+ }
451
+ } else if (inputElement.type === 'number') {
452
+ newValue = Number(inputElement.value);
453
+ } else {
454
+ newValue = inputElement.value;
455
+
456
+ // Convert string values
457
+ if (newValue === 'true') newValue = true;
458
+ if (newValue === 'false') newValue = false;
459
+ if (!isNaN(newValue) && newValue !== '' && newValue !== null) newValue = Number(newValue);
460
+ }
461
+
462
+ const oldValue = this.currentComponent[prop];
463
+
464
+ this.currentComponent[prop] = newValue;
465
+ slice.logger.logInfo('Debugger', `Updated ${prop}: ${oldValue} → ${newValue}`);
466
+
467
+ this.showVisualFeedback(inputElement);
468
+ }
469
+
470
+ /**
471
+ * Show temporary highlight on updated input.
472
+ * @param {HTMLElement} inputElement
473
+ * @returns {void}
474
+ */
475
+ showVisualFeedback(inputElement) {
476
+ const originalBorder = inputElement.style.borderColor;
477
+ const originalBoxShadow = inputElement.style.boxShadow;
478
+
479
+ inputElement.style.borderColor = '#4CAF50';
480
+ inputElement.style.boxShadow = '0 0 0 2px rgba(76, 175, 80, 0.3)';
481
+
482
+ setTimeout(() => {
483
+ inputElement.style.borderColor = originalBorder;
484
+ inputElement.style.boxShadow = originalBoxShadow;
485
+ }, 1500);
486
+ }
487
+
488
+ /**
489
+ * Apply all editable prop changes in the panel.
490
+ * @returns {void}
491
+ */
492
+ applyAllChanges() {
493
+ const inputs = this.querySelectorAll('.prop-control:not([readonly])');
494
+ let changeCount = 0;
495
+
496
+ inputs.forEach(input => {
497
+ if (!input.readOnly) {
498
+ this.applyPropertyChange(input);
499
+ changeCount++;
500
+ }
501
+ });
502
+
503
+ slice.logger.logInfo('Debugger', `Applied ${changeCount} property changes`);
504
+ this.showApplyFeedback(changeCount);
505
+ }
506
+
507
+ /**
508
+ * Show apply feedback in button.
509
+ * @param {number} changeCount
510
+ * @returns {void}
511
+ */
512
+ showApplyFeedback(changeCount) {
513
+ const applyBtn = this.querySelector('#apply-changes');
514
+ if (!applyBtn) return;
515
+
516
+ const originalText = applyBtn.textContent;
517
+
518
+ if (changeCount > 0) {
519
+ applyBtn.textContent = `✅ Applied ${changeCount} changes!`;
520
+ applyBtn.style.background = '#4CAF50';
521
+ } else {
522
+ applyBtn.textContent = '✅ No changes to apply';
523
+ applyBtn.style.background = '#9E9E9E';
524
+ }
525
+
526
+ setTimeout(() => {
527
+ applyBtn.textContent = originalText;
528
+ applyBtn.style.background = '';
529
+ }, 2000);
530
+ }
531
+
532
+ /**
533
+ * Open advanced editor for objects/functions.
534
+ * @param {string} prop
535
+ * @param {string} type
536
+ * @returns {void}
537
+ */
538
+ openAdvancedEditor(prop, type) {
539
+ this.currentEditingProp = prop;
540
+ this.currentEditingType = this.normalizeEditorType(type) || 'json';
541
+
542
+ const value = this.currentComponent[prop];
543
+
544
+ this.modalTitle.textContent = `Edit ${prop} (${this.currentEditingType})`;
545
+
546
+ this.querySelectorAll('.type-btn').forEach(btn => {
547
+ btn.classList.toggle('active', btn.dataset.type === this.currentEditingType);
548
+ });
549
+
550
+ if (this.currentEditingType === 'function') {
551
+ if (typeof value === 'function') {
552
+ this.propertyEditor.value = value.toString();
553
+ } else {
554
+ this.propertyEditor.value = 'function() {\n // Your code here\n}';
555
+ }
556
+ } else {
557
+ try {
558
+ this.propertyEditor.value = JSON.stringify(value, null, 2);
559
+ } catch (error) {
560
+ this.propertyEditor.value = 'null';
561
+ }
562
+ }
563
+
564
+ this.editorModal.classList.add('active');
565
+ this.propertyEditor.focus();
566
+ }
567
+
568
+ /**
569
+ * Validate editor contents for current type.
570
+ * @returns {void}
571
+ */
572
+ validateEditor() {
573
+ const value = this.propertyEditor.value.trim();
574
+ const type = this.normalizeEditorType(this.currentEditingType) || 'json';
575
+
576
+ try {
577
+ if (type === 'function') {
578
+ new Function('return ' + value)();
579
+ } else {
580
+ JSON.parse(value);
581
+ }
582
+
583
+ this.validationMessage.textContent = '✅ Valid syntax';
584
+ this.validationMessage.style.color = '#4CAF50';
585
+ this.querySelector('#modal-save').disabled = false;
586
+ } catch (error) {
587
+ this.validationMessage.textContent = `❌ ${error.message}`;
588
+ this.validationMessage.style.color = '#F44336';
589
+ this.querySelector('#modal-save').disabled = true;
590
+ }
591
+ }
592
+
593
+ /**
594
+ * Save editor value to component.
595
+ * @returns {void}
596
+ */
597
+ savePropertyValue() {
598
+ const value = this.propertyEditor.value.trim();
599
+ const type = this.normalizeEditorType(this.currentEditingType) || 'json';
600
+
601
+ try {
602
+ let newValue;
603
+
604
+ if (type === 'function') {
605
+ newValue = new Function('return ' + value)();
606
+ } else {
607
+ newValue = JSON.parse(value);
608
+ }
609
+
610
+ this.currentComponent[this.currentEditingProp] = newValue;
611
+ this.closeModal();
612
+
613
+ slice.logger.logInfo('Debugger', `Updated ${this.currentEditingProp} via advanced editor`);
614
+
615
+ const input = this.querySelector(`[data-prop="${this.currentEditingProp}"]`);
616
+ if (input) {
617
+ input.value = this.serializeValue(newValue);
618
+ this.showVisualFeedback(input);
619
+ }
620
+
621
+ } catch (error) {
622
+ this.validationMessage.textContent = `❌ ${error.message}`;
623
+ this.validationMessage.style.color = '#F44336';
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Close the advanced editor modal.
629
+ * @returns {void}
630
+ */
631
+ closeModal() {
632
+ this.editorModal.classList.remove('active');
633
+ this.currentEditingProp = null;
634
+ this.currentEditingType = null;
635
+ this.validationMessage.textContent = '';
636
+ }
637
+
638
+ /**
639
+ * Reset props to defaults (if defined in static props).
640
+ * @returns {void}
641
+ */
642
+ resetValues() {
643
+ const ComponentClass = this.currentComponent.constructor;
644
+ const configuredProps = ComponentClass.props || {};
645
+ let resetCount = 0;
646
+
647
+ Object.entries(configuredProps).forEach(([prop, config]) => {
648
+ if (config.default !== undefined) {
649
+ this.currentComponent[prop] = config.default;
650
+
651
+ const input = this.querySelector(`[data-prop="${prop}"]`);
652
+ if (input && !input.readOnly) {
653
+ if (input.type === 'checkbox') {
654
+ input.checked = config.default;
655
+ const label = input.parentNode.querySelector('.checkbox-label');
656
+ if (label) {
657
+ label.textContent = config.default ? 'true' : 'false';
658
+ }
659
+ } else {
660
+ input.value = this.serializeValue(config.default);
661
+ }
662
+ resetCount++;
663
+ }
664
+ }
665
+ });
666
+
667
+ slice.logger.logInfo('Debugger', 'Reset values to defaults');
668
+ this.showResetFeedback(resetCount);
669
+ }
670
+
671
+ /**
672
+ * Show reset feedback in button.
673
+ * @param {number} resetCount
674
+ * @returns {void}
675
+ */
676
+ showResetFeedback(resetCount) {
677
+ const resetBtn = this.querySelector('#reset-values');
678
+ if (!resetBtn) return;
679
+
680
+ const originalText = resetBtn.textContent;
681
+
682
+ resetBtn.textContent = `🔄 Reset ${resetCount} values!`;
683
+ resetBtn.style.background = '#FF9800';
684
+
685
+ setTimeout(() => {
686
+ resetBtn.textContent = originalText;
687
+ resetBtn.style.background = '';
688
+ }, 1500);
689
+ }
690
+
691
+ /**
692
+ * Render info tab content.
693
+ * @returns {void}
694
+ */
695
+ updateInfoTab() {
696
+ const infoContainer = this.querySelector('.info-list');
697
+ if (!infoContainer) return;
698
+
699
+ const component = this.currentComponent;
700
+
701
+ const info = [
702
+ { label: 'Component Type', value: component.constructor.name },
703
+ { label: 'Slice ID', value: component.sliceId || 'Not assigned' },
704
+ { label: 'Tag Name', value: component.tagName },
705
+ { label: 'Connected', value: component.isConnected ? 'Yes' : 'No' },
706
+ { label: 'Props Count', value: Object.keys(this.componentProps).length },
707
+ { label: 'Children', value: component.children.length }
708
+ ];
709
+
710
+ infoContainer.innerHTML = info.map(item => `
711
+ <div class="info-item">
712
+ <span class="info-label">${item.label}</span>
713
+ <span class="info-value">${item.value}</span>
714
+ </div>
715
+ `).join('');
716
+ }
717
+
718
+ /**
719
+ * Get a simple value type label.
720
+ * @param {any} value
721
+ * @returns {string}
722
+ */
723
+ getValueType(value) {
724
+ if (value === null) return 'null';
725
+ if (value === undefined) return 'undefined';
726
+ if (Array.isArray(value)) return 'array';
727
+ return typeof value;
728
+ }
729
+
730
+ /**
731
+ * Serialize a value for input display.
732
+ * @param {any} value
733
+ * @returns {string}
734
+ */
735
+ serializeValue(value) {
736
+ if (value === null || value === undefined) {
737
+ return '';
738
+ }
739
+
740
+ if (typeof value === 'object' || typeof value === 'function') {
741
+ try {
742
+ return JSON.stringify(value);
743
+ } catch {
744
+ return String(value);
745
+ }
746
+ }
747
+
748
+ return String(value);
749
+ }
750
+
751
+ /**
752
+ * Resolve which props to show in the debugger.
753
+ * @param {HTMLElement} component
754
+ * @returns {string[]}
755
+ */
756
+ getComponentPropsForDebugger(component) {
757
+ const ComponentClass = component.constructor;
758
+
759
+ if (ComponentClass.props) {
760
+ return Object.keys(ComponentClass.props);
761
+ }
762
+
763
+ if (component.debuggerProps && Array.isArray(component.debuggerProps)) {
764
+ return component.debuggerProps;
765
+ }
766
+
767
+ return this.detectUsedProps(component);
768
+ }
769
+
770
+ /**
771
+ * Detect props from backing fields on the component.
772
+ * @param {HTMLElement} component
773
+ * @returns {string[]}
774
+ */
775
+ detectUsedProps(component) {
776
+ const usedProps = [];
777
+
778
+ Object.getOwnPropertyNames(component).forEach(key => {
779
+ if (key.startsWith('_') && key !== '_isActive') {
780
+ const propName = key.substring(1);
781
+ usedProps.push(propName);
782
+ }
783
+ });
784
+
785
+ return usedProps;
786
+ }
787
+
788
+ /**
789
+ * Hide the debugger UI.
790
+ * @returns {void}
791
+ */
792
+ hide() {
793
+ this.debuggerContainer.classList.remove('active');
794
+ this.closeModal();
795
+ }
796
+ }
797
+
798
+ customElements.define('slice-debugger', Debugger);
799
+
800
+ function productionOnlyCSS(){
801
+ return `
802
+ #debugger-container {
803
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
804
+ display: none;
805
+ position: fixed;
806
+ top: 20px;
807
+ right: 20px;
808
+ width: min(420px, calc(100vw - 40px));
809
+ height: 85vh;
810
+ max-height: 85vh;
811
+ background: var(--primary-background-color);
812
+ border: 1px solid var(--medium-color);
813
+ border-radius: 12px;
814
+ box-shadow: 0 20px 40px rgba(var(--primary-color-rgb), 0.15), 0 4px 12px rgba(0, 0, 0, 0.1);
815
+ z-index: 10000;
816
+ overflow: hidden;
817
+ backdrop-filter: blur(10px);
818
+ box-sizing: border-box;
819
+ }
820
+
821
+ #debugger-container.active {
822
+ display: flex;
823
+ flex-direction: column;
824
+ }
825
+
826
+ .debugger-header {
827
+ background: linear-gradient(135deg, var(--primary-color), var(--primary-color-shade));
828
+ color: var(--primary-color-contrast);
829
+ padding: 12px 16px;
830
+ border-radius: 12px 12px 0 0;
831
+ user-select: none;
832
+ cursor: grab;
833
+ }
834
+
835
+ .debugger-header:active {
836
+ cursor: grabbing;
837
+ }
838
+
839
+ .header-content {
840
+ display: flex;
841
+ justify-content: space-between;
842
+ align-items: center;
843
+ }
844
+
845
+ .component-info {
846
+ display: flex;
847
+ align-items: center;
848
+ gap: 10px;
849
+ }
850
+
851
+ .component-icon {
852
+ font-size: 20px;
853
+ opacity: 0.9;
854
+ }
855
+
856
+ .component-name {
857
+ font-size: 14px;
858
+ font-weight: 600;
859
+ margin-bottom: 2px;
860
+ }
861
+
862
+ .component-id {
863
+ font-size: 11px;
864
+ opacity: 0.8;
865
+ }
866
+
867
+ .header-actions {
868
+ display: flex;
869
+ gap: 8px;
870
+ }
871
+
872
+ .minimize-btn, #close-debugger {
873
+ background: rgba(255, 255, 255, 0.2);
874
+ border: none;
875
+ color: var(--primary-color-contrast);
876
+ width: 28px;
877
+ height: 28px;
878
+ border-radius: 6px;
879
+ cursor: pointer;
880
+ display: flex;
881
+ align-items: center;
882
+ justify-content: center;
883
+ font-size: 16px;
884
+ font-weight: bold;
885
+ transition: background 0.2s ease;
886
+ }
887
+
888
+ .minimize-btn:hover, #close-debugger:hover {
889
+ background: rgba(255, 255, 255, 0.3);
890
+ }
891
+
892
+ .debugger-content {
893
+ flex: 1;
894
+ display: flex;
895
+ flex-direction: column;
896
+ overflow: hidden;
897
+ }
898
+
899
+ .tabs-container {
900
+ border-bottom: 1px solid var(--medium-color);
901
+ }
902
+
903
+ .tab-nav {
904
+ display: flex;
905
+ background: var(--tertiary-background-color);
906
+ }
907
+
908
+ .tab-btn {
909
+ flex: 1;
910
+ padding: 10px 14px;
911
+ border: none;
912
+ background: transparent;
913
+ color: var(--font-secondary-color);
914
+ font-size: 12px;
915
+ font-weight: 500;
916
+ cursor: pointer;
917
+ transition: all 0.2s ease;
918
+ border-bottom: 2px solid transparent;
919
+ }
920
+
921
+ .tab-btn:hover {
922
+ background: var(--secondary-background-color);
923
+ color: var(--font-primary-color);
924
+ }
925
+
926
+ .tab-btn.active {
927
+ background: var(--primary-background-color);
928
+ color: var(--primary-color);
929
+ border-bottom-color: var(--primary-color);
930
+ font-weight: 600;
931
+ }
932
+
933
+ .tab-content {
934
+ flex: 1;
935
+ overflow: hidden;
936
+ min-height: 0;
937
+ }
938
+
939
+ .tab-pane {
940
+ display: none;
941
+ height: 100%;
942
+ overflow-y: auto;
943
+ overflow-x: hidden;
944
+ padding: 16px;
945
+ box-sizing: border-box;
946
+ }
947
+
948
+ .tab-pane.active {
949
+ display: block;
950
+ }
951
+
952
+ .tab-pane::-webkit-scrollbar {
953
+ width: 4px;
954
+ }
955
+
956
+ .tab-pane::-webkit-scrollbar-track {
957
+ background: var(--tertiary-background-color);
958
+ border-radius: 2px;
959
+ }
960
+
961
+ .tab-pane::-webkit-scrollbar-thumb {
962
+ background: var(--medium-color);
963
+ border-radius: 2px;
964
+ }
965
+
966
+ .tab-pane::-webkit-scrollbar-thumb:hover {
967
+ background: var(--primary-color);
968
+ }
969
+
970
+ .props-container {
971
+ display: flex;
972
+ flex-direction: column;
973
+ gap: 12px;
974
+ margin-bottom: 16px;
975
+ min-width: 0;
976
+ overflow-x: hidden;
977
+ }
978
+
979
+ #debugger-container *,
980
+ #debugger-container *::before,
981
+ #debugger-container *::after {
982
+ box-sizing: border-box;
983
+ }
984
+
985
+ .props-actions {
986
+ border-top: 1px solid var(--medium-color);
987
+ padding-top: 16px;
988
+ margin-top: 8px;
989
+ }
990
+
991
+ .actions-note {
992
+ margin-top: 12px;
993
+ padding: 8px 12px;
994
+ background: var(--tertiary-background-color);
995
+ border-radius: 6px;
996
+ border: 1px solid var(--medium-color);
997
+ }
998
+
999
+ .actions-note small {
1000
+ color: var(--font-secondary-color);
1001
+ font-size: 11px;
1002
+ display: flex;
1003
+ align-items: center;
1004
+ gap: 6px;
1005
+ }
1006
+
1007
+ .props-section {
1008
+ background: var(--tertiary-background-color);
1009
+ border: 1px solid var(--medium-color);
1010
+ border-radius: 8px;
1011
+ padding: 12px;
1012
+ }
1013
+
1014
+ .section-title {
1015
+ font-size: 12px;
1016
+ font-weight: 600;
1017
+ color: var(--font-primary-color);
1018
+ margin-bottom: 12px;
1019
+ display: flex;
1020
+ align-items: center;
1021
+ gap: 6px;
1022
+ }
1023
+
1024
+ .prop-item {
1025
+ display: flex;
1026
+ flex-direction: column;
1027
+ gap: 6px;
1028
+ padding: 12px;
1029
+ background: var(--primary-background-color);
1030
+ border: 1px solid var(--medium-color);
1031
+ border-radius: 6px;
1032
+ margin-bottom: 8px;
1033
+ transition: border-color 0.2s ease;
1034
+ min-width: 0;
1035
+ overflow: hidden;
1036
+ }
1037
+
1038
+ .prop-item:hover {
1039
+ border-color: var(--primary-color);
1040
+ }
1041
+
1042
+ .prop-header {
1043
+ display: flex;
1044
+ justify-content: space-between;
1045
+ align-items: center;
1046
+ gap: 8px;
1047
+ min-width: 0;
1048
+ }
1049
+
1050
+ .prop-title {
1051
+ display: flex;
1052
+ align-items: center;
1053
+ gap: 6px;
1054
+ min-width: 0;
1055
+ flex: 1;
1056
+ }
1057
+
1058
+ .prop-title strong {
1059
+ min-width: 0;
1060
+ overflow: hidden;
1061
+ text-overflow: ellipsis;
1062
+ white-space: nowrap;
1063
+ }
1064
+
1065
+ .prop-name {
1066
+ font-size: 13px;
1067
+ font-weight: 600;
1068
+ color: var(--font-primary-color);
1069
+ }
1070
+
1071
+ .prop-name.required::after {
1072
+ content: " *";
1073
+ color: var(--danger-color);
1074
+ }
1075
+
1076
+ .prop-meta {
1077
+ display: flex;
1078
+ align-items: center;
1079
+ gap: 8px;
1080
+ }
1081
+
1082
+ .prop-type {
1083
+ font-size: 11px;
1084
+ padding: 2px 6px;
1085
+ background: var(--secondary-color);
1086
+ color: var(--secondary-color-contrast);
1087
+ border-radius: 4px;
1088
+ font-family: monospace;
1089
+ font-weight: 500;
1090
+ flex-shrink: 0;
1091
+ }
1092
+
1093
+ .prop-status {
1094
+ font-size: 12px;
1095
+ font-weight: 500;
1096
+ flex-shrink: 0;
1097
+ }
1098
+
1099
+ .status-used {
1100
+ color: var(--success-color);
1101
+ }
1102
+
1103
+ .status-missing {
1104
+ color: var(--danger-color);
1105
+ }
1106
+
1107
+ .status-optional {
1108
+ color: var(--medium-color);
1109
+ }
1110
+
1111
+ .prop-input {
1112
+ margin-top: 8px;
1113
+ min-width: 0;
1114
+ overflow: hidden;
1115
+ }
1116
+
1117
+ .input-group {
1118
+ position: relative;
1119
+ max-width: 100%;
1120
+ min-width: 0;
1121
+ overflow: hidden;
1122
+ }
1123
+
1124
+ .prop-control {
1125
+ width: 100%;
1126
+ max-width: 100%;
1127
+ min-width: 0;
1128
+ padding: 8px 32px 8px 10px;
1129
+ border: 1px solid var(--medium-color);
1130
+ border-radius: 6px;
1131
+ background: var(--primary-background-color);
1132
+ color: var(--font-primary-color);
1133
+ font-size: 13px;
1134
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1135
+ font-family: monospace;
1136
+ box-sizing: border-box;
1137
+ display: block;
1138
+ }
1139
+
1140
+ .prop-control:focus {
1141
+ outline: none;
1142
+ border-color: var(--primary-color);
1143
+ box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.1);
1144
+ }
1145
+
1146
+ .prop-control:disabled {
1147
+ background: var(--disabled-color);
1148
+ color: var(--font-secondary-color);
1149
+ cursor: not-allowed;
1150
+ }
1151
+
1152
+ .prop-control[readonly] {
1153
+ background: var(--tertiary-background-color);
1154
+ cursor: pointer;
1155
+ text-overflow: ellipsis;
1156
+ overflow: hidden;
1157
+ white-space: nowrap;
1158
+ max-width: 100%;
1159
+ }
1160
+
1161
+ .prop-control[readonly]:focus {
1162
+ border-color: var(--accent-color);
1163
+ box-shadow: 0 0 0 3px rgba(var(--accent-color), 0.1);
1164
+ }
1165
+
1166
+ /* Estilos específicos para checkboxes */
1167
+ .prop-control[type="checkbox"] {
1168
+ width: auto;
1169
+ padding: 0;
1170
+ margin: 0;
1171
+ cursor: pointer;
1172
+ }
1173
+
1174
+ .edit-btn {
1175
+ position: absolute;
1176
+ right: 4px;
1177
+ top: 50%;
1178
+ transform: translateY(-50%);
1179
+ background: var(--accent-color);
1180
+ border: none;
1181
+ color: white;
1182
+ width: 24px;
1183
+ height: 24px;
1184
+ border-radius: 4px;
1185
+ cursor: pointer;
1186
+ font-size: 12px;
1187
+ display: flex;
1188
+ align-items: center;
1189
+ justify-content: center;
1190
+ transition: background 0.2s ease;
1191
+ }
1192
+
1193
+ .edit-btn:hover {
1194
+ background: var(--primary-color);
1195
+ }
1196
+
1197
+ .default-value {
1198
+ font-size: 11px;
1199
+ color: var(--font-secondary-color);
1200
+ font-style: italic;
1201
+ margin-top: 4px;
1202
+ }
1203
+
1204
+ .info-list {
1205
+ display: flex;
1206
+ flex-direction: column;
1207
+ gap: 12px;
1208
+ }
1209
+
1210
+ .info-item {
1211
+ display: flex;
1212
+ justify-content: space-between;
1213
+ padding: 12px;
1214
+ background: var(--tertiary-background-color);
1215
+ border-radius: 6px;
1216
+ border: 1px solid var(--medium-color);
1217
+ }
1218
+
1219
+ .info-label {
1220
+ font-weight: 600;
1221
+ color: var(--font-primary-color);
1222
+ font-size: 13px;
1223
+ }
1224
+
1225
+ .info-value {
1226
+ color: var(--font-secondary-color);
1227
+ font-family: monospace;
1228
+ font-size: 12px;
1229
+ }
1230
+
1231
+ .actions-container {
1232
+ display: flex;
1233
+ flex-direction: column;
1234
+ gap: 16px;
1235
+ }
1236
+
1237
+ .action-buttons {
1238
+ display: flex;
1239
+ flex-direction: column;
1240
+ gap: 8px;
1241
+ }
1242
+
1243
+ .action-btn {
1244
+ padding: 12px 16px;
1245
+ border: none;
1246
+ border-radius: 6px;
1247
+ font-size: 13px;
1248
+ font-weight: 500;
1249
+ cursor: pointer;
1250
+ transition: all 0.2s ease;
1251
+ display: flex;
1252
+ align-items: center;
1253
+ justify-content: center;
1254
+ gap: 8px;
1255
+ }
1256
+
1257
+ .action-btn.primary {
1258
+ background: var(--primary-color);
1259
+ color: var(--primary-color-contrast);
1260
+ }
1261
+
1262
+ .action-btn.primary:hover {
1263
+ background: var(--primary-color-shade);
1264
+ }
1265
+
1266
+ .action-btn.secondary {
1267
+ background: var(--secondary-color);
1268
+ color: var(--secondary-color-contrast);
1269
+ }
1270
+
1271
+ .action-btn.secondary:hover {
1272
+ opacity: 0.9;
1273
+ }
1274
+
1275
+ .action-btn.tertiary {
1276
+ background: var(--tertiary-background-color);
1277
+ color: var(--font-primary-color);
1278
+ border: 1px solid var(--medium-color);
1279
+ }
1280
+
1281
+ .action-btn.tertiary:hover {
1282
+ background: var(--secondary-background-color);
1283
+ }
1284
+
1285
+ /* Modal Styles */
1286
+ .editor-modal {
1287
+ display: none;
1288
+ position: fixed;
1289
+ top: 0;
1290
+ left: 0;
1291
+ width: 100%;
1292
+ height: 100%;
1293
+ background: rgba(0, 0, 0, 0.6);
1294
+ z-index: 20000;
1295
+ backdrop-filter: blur(4px);
1296
+ }
1297
+
1298
+ .editor-modal.active {
1299
+ display: flex;
1300
+ align-items: center;
1301
+ justify-content: center;
1302
+ }
1303
+
1304
+ .modal-content {
1305
+ background: var(--primary-background-color);
1306
+ border-radius: 12px;
1307
+ width: min(600px, 92vw);
1308
+ max-width: 600px;
1309
+ max-height: 80%;
1310
+ display: flex;
1311
+ flex-direction: column;
1312
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
1313
+ border: 1px solid var(--medium-color);
1314
+ box-sizing: border-box;
1315
+ }
1316
+
1317
+ .modal-header {
1318
+ padding: 16px 20px;
1319
+ background: var(--tertiary-background-color);
1320
+ border-radius: 12px 12px 0 0;
1321
+ border-bottom: 1px solid var(--medium-color);
1322
+ display: flex;
1323
+ justify-content: space-between;
1324
+ align-items: center;
1325
+ }
1326
+
1327
+ .modal-header h3 {
1328
+ margin: 0;
1329
+ font-size: 16px;
1330
+ font-weight: 600;
1331
+ color: var(--font-primary-color);
1332
+ }
1333
+
1334
+ .modal-close {
1335
+ background: none;
1336
+ border: none;
1337
+ font-size: 20px;
1338
+ color: var(--font-secondary-color);
1339
+ cursor: pointer;
1340
+ width: 32px;
1341
+ height: 32px;
1342
+ border-radius: 6px;
1343
+ display: flex;
1344
+ align-items: center;
1345
+ justify-content: center;
1346
+ transition: background 0.2s ease;
1347
+ }
1348
+
1349
+ .modal-close:hover {
1350
+ background: var(--secondary-background-color);
1351
+ }
1352
+
1353
+ .modal-body {
1354
+ flex: 1;
1355
+ padding: 20px;
1356
+ display: flex;
1357
+ flex-direction: column;
1358
+ gap: 16px;
1359
+ overflow: hidden;
1360
+ min-height: 0;
1361
+ }
1362
+
1363
+ .editor-type-selector {
1364
+ display: flex;
1365
+ gap: 4px;
1366
+ background: var(--tertiary-background-color);
1367
+ padding: 4px;
1368
+ border-radius: 6px;
1369
+ }
1370
+
1371
+ .type-btn {
1372
+ flex: 1;
1373
+ padding: 8px 12px;
1374
+ border: none;
1375
+ background: transparent;
1376
+ color: var(--font-secondary-color);
1377
+ font-size: 12px;
1378
+ font-weight: 500;
1379
+ cursor: pointer;
1380
+ border-radius: 4px;
1381
+ transition: all 0.2s ease;
1382
+ }
1383
+
1384
+ .type-btn.active {
1385
+ background: var(--primary-color);
1386
+ color: var(--primary-color-contrast);
1387
+ }
1388
+
1389
+ .editor-container {
1390
+ flex: 1;
1391
+ position: relative;
1392
+ min-height: 200px;
1393
+ min-width: 0;
1394
+ }
1395
+
1396
+ #property-editor {
1397
+ width: 100%;
1398
+ height: 100%;
1399
+ border: 1px solid var(--medium-color);
1400
+ border-radius: 6px;
1401
+ padding: 12px;
1402
+ background: var(--primary-background-color);
1403
+ color: var(--font-primary-color);
1404
+ font-family: 'Monaco', 'Consolas', monospace;
1405
+ font-size: 13px;
1406
+ line-height: 1.5;
1407
+ resize: none;
1408
+ outline: none;
1409
+ min-height: 200px;
1410
+ box-sizing: border-box;
1411
+ }
1412
+
1413
+ #property-editor:focus {
1414
+ border-color: var(--primary-color);
1415
+ box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.1);
1416
+ }
1417
+
1418
+ .validation-message {
1419
+ font-size: 12px;
1420
+ color: var(--danger-color);
1421
+ min-height: 18px;
1422
+ display: flex;
1423
+ align-items: center;
1424
+ gap: 6px;
1425
+ }
1426
+
1427
+ .modal-actions {
1428
+ padding: 16px 20px;
1429
+ background: var(--tertiary-background-color);
1430
+ border-radius: 0 0 12px 12px;
1431
+ border-top: 1px solid var(--medium-color);
1432
+ display: flex;
1433
+ gap: 12px;
1434
+ justify-content: flex-end;
1435
+ }
1436
+
1437
+ .modal-btn {
1438
+ padding: 10px 20px;
1439
+ border: none;
1440
+ border-radius: 6px;
1441
+ font-size: 13px;
1442
+ font-weight: 500;
1443
+ cursor: pointer;
1444
+ transition: all 0.2s ease;
1445
+ }
1446
+
1447
+ .modal-btn.cancel {
1448
+ background: var(--tertiary-background-color);
1449
+ color: var(--font-primary-color);
1450
+ border: 1px solid var(--medium-color);
1451
+ }
1452
+
1453
+ .modal-btn.cancel:hover {
1454
+ background: var(--secondary-background-color);
1455
+ }
1456
+
1457
+ .modal-btn.save {
1458
+ background: var(--success-color);
1459
+ color: var(--success-contrast);
1460
+ }
1461
+
1462
+ .modal-btn.save:hover {
1463
+ opacity: 0.9;
1464
+ }
1465
+
1466
+ .modal-btn.save:disabled {
1467
+ background: var(--disabled-color);
1468
+ cursor: not-allowed;
1469
+ }
1470
+ `
1471
+ }
1472
+
1473
+ function productionOnlyHtml(){
1474
+ return `
1475
+ <div id="debugger-container">
1476
+ <div class="debugger-header">
1477
+ <div class="header-content">
1478
+ <div class="component-info">
1479
+ <div class="component-icon">🔍</div>
1480
+ <div class="component-details">
1481
+ <div class="component-name">Component Inspector</div>
1482
+ <div class="component-id">Ready to debug</div>
1483
+ </div>
1484
+ </div>
1485
+ <div class="header-actions">
1486
+ <button class="minimize-btn" title="Minimize">−</button>
1487
+ <button id="close-debugger" title="Close">×</button>
1488
+ </div>
1489
+ </div>
1490
+ </div>
1491
+
1492
+ <div class="debugger-content">
1493
+ <div class="tabs-container">
1494
+ <div class="tab-nav">
1495
+ <button class="tab-btn active" data-tab="props">📋 Props</button>
1496
+ <button class="tab-btn" data-tab="info">â„šī¸ Info</button>
1497
+ </div>
1498
+ </div>
1499
+
1500
+ <div class="tab-content">
1501
+ <div class="tab-pane active" id="props-tab">
1502
+ <div class="props-container"></div>
1503
+ <div class="props-actions">
1504
+ <div class="action-buttons">
1505
+ <button class="action-btn primary" id="apply-changes">✅ Apply Changes</button>
1506
+ <button class="action-btn secondary" id="reset-values">🔄 Reset Values</button>
1507
+ </div>
1508
+ <div class="actions-note">
1509
+ <small>💡 Press Enter on any input to apply changes automatically</small>
1510
+ </div>
1511
+ </div>
1512
+ </div>
1513
+
1514
+ <div class="tab-pane" id="info-tab">
1515
+ <div class="info-container">
1516
+ <div class="info-list"></div>
1517
+ </div>
1518
+ </div>
1519
+ </div>
1520
+ </div>
1521
+
1522
+ <!-- Modal para editar objetos/funciones -->
1523
+ <div class="editor-modal" id="editor-modal">
1524
+ <div class="modal-content">
1525
+ <div class="modal-header">
1526
+ <h3 id="modal-title">Edit Property</h3>
1527
+ <button class="modal-close" id="modal-close">×</button>
1528
+ </div>
1529
+ <div class="modal-body">
1530
+ <div class="editor-type-selector">
1531
+ <button class="type-btn active" data-type="json">📋 JSON</button>
1532
+ <button class="type-btn" data-type="function">⚡ Function</button>
1533
+ </div>
1534
+ <div class="editor-container">
1535
+ <textarea id="property-editor" spellcheck="false"></textarea>
1536
+ </div>
1537
+ <div class="editor-footer">
1538
+ <div class="validation-message"></div>
1539
+ </div>
1540
+ </div>
1541
+ <div class="modal-actions">
1542
+ <button class="modal-btn cancel" id="modal-cancel">Cancel</button>
1543
+ <button class="modal-btn save" id="modal-save">Save Changes</button>
1544
+ </div>
1545
+ </div>
1546
+ </div>
1547
+ </div>`
1548
1548
  }