slicejs-web-framework 2.3.0 → 2.3.2

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