slicejs-web-framework 1.0.31 → 1.0.32
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,3 +1,5 @@
|
|
|
1
|
+
// ✅ VERSIÓN ANTI-INTERFERENCIA - Aislada del Router y con debugging
|
|
2
|
+
|
|
1
3
|
export default class Debugger extends HTMLElement {
|
|
2
4
|
constructor() {
|
|
3
5
|
super();
|
|
@@ -6,10 +8,16 @@ export default class Debugger extends HTMLElement {
|
|
|
6
8
|
this.selectedComponentSliceId = null;
|
|
7
9
|
this.isActive = false;
|
|
8
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;
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
async enableDebugMode() {
|
|
12
|
-
// Cargar HTML y CSS desde archivos externos
|
|
13
21
|
const html = await slice.controller.fetchText('Debugger', 'html', 'Structural');
|
|
14
22
|
const css = await slice.controller.fetchText('Debugger', 'css', 'Structural');
|
|
15
23
|
|
|
@@ -20,7 +28,7 @@ export default class Debugger extends HTMLElement {
|
|
|
20
28
|
this.setupEventListeners();
|
|
21
29
|
this.makeDraggable();
|
|
22
30
|
|
|
23
|
-
slice.logger.logInfo('
|
|
31
|
+
slice.logger.logInfo('Debugger', 'Advanced Debug mode enabled');
|
|
24
32
|
return true;
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -74,11 +82,13 @@ export default class Debugger extends HTMLElement {
|
|
|
74
82
|
});
|
|
75
83
|
|
|
76
84
|
// Action buttons
|
|
77
|
-
this.querySelector('#apply-changes')
|
|
85
|
+
this.querySelector('#apply-changes')?.addEventListener('click', (e) => {
|
|
86
|
+
e.stopPropagation();
|
|
78
87
|
this.applyAllChanges();
|
|
79
88
|
});
|
|
80
89
|
|
|
81
|
-
this.querySelector('#reset-values')
|
|
90
|
+
this.querySelector('#reset-values')?.addEventListener('click', (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
82
92
|
this.resetValues();
|
|
83
93
|
});
|
|
84
94
|
|
|
@@ -94,59 +104,73 @@ export default class Debugger extends HTMLElement {
|
|
|
94
104
|
}
|
|
95
105
|
});
|
|
96
106
|
|
|
97
|
-
// ✅
|
|
107
|
+
// ✅ EVENTOS PRINCIPALES - Con protección anti-interferencia
|
|
108
|
+
this.addEventListener('mousedown', (event) => {
|
|
109
|
+
if (event.target.classList.contains('prop-control')) {
|
|
110
|
+
this.isDebuggerInput = true;
|
|
111
|
+
// Prevenir interferencias del Router u otros sistemas
|
|
112
|
+
event.stopPropagation();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.addEventListener('focus', (event) => {
|
|
117
|
+
if (event.target.classList.contains('prop-control')) {
|
|
118
|
+
this.isDebuggerInput = true;
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
}
|
|
121
|
+
}, true);
|
|
122
|
+
|
|
123
|
+
this.addEventListener('blur', (event) => {
|
|
124
|
+
if (event.target.classList.contains('prop-control')) {
|
|
125
|
+
this.isDebuggerInput = false;
|
|
126
|
+
}
|
|
127
|
+
}, true);
|
|
128
|
+
|
|
98
129
|
this.addEventListener('keypress', (event) => {
|
|
99
130
|
if (event.key === 'Enter' && event.target.classList.contains('prop-control')) {
|
|
100
131
|
event.preventDefault();
|
|
132
|
+
event.stopPropagation();
|
|
101
133
|
this.applyPropertyChange(event.target);
|
|
102
134
|
}
|
|
103
135
|
});
|
|
104
136
|
|
|
105
|
-
// ✅ SIMPLIFICADO: Solo cambio visual para checkboxes
|
|
106
137
|
this.addEventListener('change', (event) => {
|
|
107
138
|
if (event.target.type === 'checkbox' && event.target.classList.contains('prop-control')) {
|
|
108
|
-
|
|
109
|
-
if (span) {
|
|
110
|
-
span.textContent = event.target.checked ? 'true' : 'false';
|
|
111
|
-
}
|
|
139
|
+
event.stopPropagation();
|
|
112
140
|
this.applyPropertyChange(event.target);
|
|
113
141
|
}
|
|
114
142
|
});
|
|
115
143
|
|
|
116
|
-
// ✅
|
|
144
|
+
// ✅ PROTECCIÓN GLOBAL: Prevenir que eventos externos interfieran
|
|
117
145
|
this.addEventListener('click', (event) => {
|
|
118
|
-
if (event.target
|
|
119
|
-
|
|
146
|
+
if (this.contains(event.target)) {
|
|
147
|
+
event.stopPropagation();
|
|
120
148
|
}
|
|
121
149
|
});
|
|
122
150
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
console.log('🎯 Debug: Input focused', event.target.dataset.prop);
|
|
126
|
-
}
|
|
127
|
-
}, true);
|
|
128
|
-
|
|
129
|
-
this.addEventListener('blur', (event) => {
|
|
130
|
-
if (event.target.classList.contains('prop-control')) {
|
|
131
|
-
console.log('🎯 Debug: Input blurred', event.target.dataset.prop);
|
|
132
|
-
}
|
|
133
|
-
}, true);
|
|
151
|
+
// ✅ Los eventos DOMNodeInserted/Removed están deprecated,
|
|
152
|
+
// pero la protección con stopPropagation() ya es suficiente
|
|
134
153
|
}
|
|
135
154
|
|
|
136
155
|
switchTab(tabName) {
|
|
137
156
|
this.activeTab = tabName;
|
|
138
157
|
|
|
139
|
-
// Update tab buttons
|
|
140
158
|
this.querySelectorAll('.tab-btn').forEach(btn => {
|
|
141
159
|
btn.classList.toggle('active', btn.dataset.tab === tabName);
|
|
142
160
|
});
|
|
143
161
|
|
|
144
|
-
// Update tab panes
|
|
145
162
|
this.querySelectorAll('.tab-pane').forEach(pane => {
|
|
146
163
|
pane.classList.toggle('active', pane.id === `${tabName}-tab`);
|
|
147
164
|
});
|
|
148
165
|
}
|
|
149
166
|
|
|
167
|
+
switchEditorType(type) {
|
|
168
|
+
this.querySelectorAll('.type-btn').forEach(btn => {
|
|
169
|
+
btn.classList.toggle('active', btn.dataset.type === type);
|
|
170
|
+
});
|
|
171
|
+
this.currentEditingType = type;
|
|
172
|
+
}
|
|
173
|
+
|
|
150
174
|
attachDebugMode(component) {
|
|
151
175
|
if (this.toggleClick === 'right') {
|
|
152
176
|
this.toggle = 'contextmenu';
|
|
@@ -211,280 +235,278 @@ export default class Debugger extends HTMLElement {
|
|
|
211
235
|
}
|
|
212
236
|
});
|
|
213
237
|
|
|
238
|
+
// ✅ Crear UI sin interferencias
|
|
214
239
|
this.updateDebuggerContent();
|
|
215
240
|
this.debuggerContainer.classList.add('active');
|
|
216
241
|
}
|
|
217
242
|
|
|
218
243
|
updateDebuggerContent() {
|
|
219
|
-
console.log('🔥 DEBUG: updateDebuggerContent called - this recreates ALL inputs!');
|
|
220
|
-
console.trace(); // Esto nos dirá desde dónde se está llamando
|
|
221
244
|
this.updatePropsTab();
|
|
222
245
|
this.updateInfoTab();
|
|
223
246
|
}
|
|
224
247
|
|
|
225
248
|
updatePropsTab() {
|
|
226
|
-
|
|
227
|
-
|
|
249
|
+
const propsContainer = this.querySelector('.props-container');
|
|
250
|
+
if (!propsContainer) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
228
253
|
|
|
229
|
-
|
|
230
|
-
const ComponentClass = component.constructor;
|
|
231
|
-
const hasStaticProps = ComponentClass.props && !slice.isProduction();
|
|
232
|
-
const staticProps = ComponentClass.props || {};
|
|
233
|
-
|
|
234
|
-
this.propsContainer.innerHTML = '';
|
|
254
|
+
propsContainer.innerHTML = '';
|
|
235
255
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
usedSection.className = 'props-section';
|
|
240
|
-
usedSection.innerHTML = '<div class="section-title">📋 Component Properties</div>';
|
|
256
|
+
const realComponentProps = this.getComponentPropsForDebugger(this.currentComponent);
|
|
257
|
+
const ComponentClass = this.currentComponent.constructor;
|
|
258
|
+
const configuredProps = ComponentClass.props || {};
|
|
241
259
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
realComponentProps.forEach(prop => {
|
|
261
|
+
const propElement = this.createPropElement(prop, configuredProps[prop]);
|
|
262
|
+
propsContainer.appendChild(propElement);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
246
265
|
|
|
247
|
-
|
|
266
|
+
createPropElement(prop, config = {}) {
|
|
267
|
+
const propWrapper = document.createElement('div');
|
|
268
|
+
propWrapper.className = 'prop-item';
|
|
269
|
+
propWrapper.dataset.prop = prop;
|
|
270
|
+
|
|
271
|
+
const currentValue = this.currentComponent[prop];
|
|
272
|
+
const valueType = this.getValueType(currentValue);
|
|
273
|
+
|
|
274
|
+
// Status based on usage
|
|
275
|
+
let status, statusClass;
|
|
276
|
+
if (currentValue !== undefined && currentValue !== null) {
|
|
277
|
+
status = 'Used';
|
|
278
|
+
statusClass = 'status-used';
|
|
279
|
+
} else if (config.required) {
|
|
280
|
+
status = 'Missing';
|
|
281
|
+
statusClass = 'status-missing';
|
|
248
282
|
} else {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
legacySection.className = 'props-section';
|
|
252
|
-
legacySection.innerHTML = '<div class="section-title">📋 Properties (Legacy)</div>';
|
|
253
|
-
|
|
254
|
-
Object.entries(this.componentProps).forEach(([prop, value]) => {
|
|
255
|
-
if (value !== null) {
|
|
256
|
-
const propItem = this.createLegacyPropItem(prop, value);
|
|
257
|
-
legacySection.appendChild(propItem);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
this.propsContainer.appendChild(legacySection);
|
|
283
|
+
status = 'Optional';
|
|
284
|
+
statusClass = 'status-optional';
|
|
262
285
|
}
|
|
263
|
-
}
|
|
264
286
|
|
|
265
|
-
|
|
266
|
-
const isUsed = value !== undefined;
|
|
267
|
-
const isRequired = config.required;
|
|
268
|
-
|
|
269
|
-
const propItem = document.createElement('div');
|
|
270
|
-
propItem.className = 'prop-item';
|
|
271
|
-
|
|
272
|
-
const statusClass = isRequired && !isUsed ? 'status-missing' :
|
|
273
|
-
isUsed ? 'status-used' : 'status-optional';
|
|
274
|
-
const statusText = isRequired && !isUsed ? '❌ Missing' :
|
|
275
|
-
isUsed ? '✅ Used' : '⚪ Optional';
|
|
276
|
-
|
|
277
|
-
const inputType = this.getInputType(config.type, value);
|
|
278
|
-
const inputHtml = this.createPropertyInput(prop, value, config, inputType);
|
|
279
|
-
|
|
280
|
-
propItem.innerHTML = `
|
|
287
|
+
propWrapper.innerHTML = `
|
|
281
288
|
<div class="prop-header">
|
|
282
|
-
<div class="prop-
|
|
283
|
-
|
|
284
|
-
<span class="prop-type">${
|
|
285
|
-
<span class="prop-status ${statusClass}">${statusText}</span>
|
|
289
|
+
<div class="prop-title">
|
|
290
|
+
<strong>${prop}</strong>
|
|
291
|
+
<span class="prop-type">${valueType}</span>
|
|
286
292
|
</div>
|
|
293
|
+
<div class="prop-status ${statusClass}">${status}</div>
|
|
287
294
|
</div>
|
|
288
|
-
<div class="prop-input"
|
|
289
|
-
|
|
290
|
-
`<div class="default-value">Default: ${JSON.stringify(config.default)}</div>` : ''}
|
|
291
|
-
`;
|
|
292
|
-
|
|
293
|
-
return propItem;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
createLegacyPropItem(prop, value) {
|
|
297
|
-
const propItem = document.createElement('div');
|
|
298
|
-
propItem.className = 'prop-item';
|
|
299
|
-
|
|
300
|
-
const inputType = this.getInputType(typeof value, value);
|
|
301
|
-
const inputHtml = this.createPropertyInput(prop, value, null, inputType);
|
|
302
|
-
|
|
303
|
-
propItem.innerHTML = `
|
|
304
|
-
<div class="prop-header">
|
|
305
|
-
<div class="prop-name">${prop}</div>
|
|
306
|
-
<div class="prop-meta">
|
|
307
|
-
<span class="prop-type">${typeof value}</span>
|
|
308
|
-
<span class="prop-status status-used">✅ Used</span>
|
|
309
|
-
</div>
|
|
295
|
+
<div class="prop-input">
|
|
296
|
+
${this.createInputForType(prop, currentValue, valueType, config)}
|
|
310
297
|
</div>
|
|
311
|
-
|
|
298
|
+
${config.default !== undefined ? `<div class="default-value">Default: ${JSON.stringify(config.default)}</div>` : ''}
|
|
312
299
|
`;
|
|
313
300
|
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
getInputType(type, value) {
|
|
318
|
-
if (typeof value === 'object' || type === 'object') return 'object';
|
|
319
|
-
if (typeof value === 'function' || type === 'function') return 'function';
|
|
320
|
-
if (typeof value === 'boolean' || type === 'boolean') return 'boolean';
|
|
321
|
-
if (typeof value === 'number' || type === 'number') return 'number';
|
|
322
|
-
return 'text';
|
|
301
|
+
return propWrapper;
|
|
323
302
|
}
|
|
324
303
|
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
const displayValue = this.formatValueForDisplay(value);
|
|
304
|
+
createInputForType(prop, value, type, config = {}) {
|
|
305
|
+
const serializedValue = this.serializeValue(value);
|
|
328
306
|
|
|
329
|
-
if (
|
|
307
|
+
if (type === 'boolean') {
|
|
330
308
|
return `
|
|
331
309
|
<div class="input-group">
|
|
332
|
-
<input type="
|
|
333
|
-
|
|
310
|
+
<input type="checkbox"
|
|
311
|
+
class="prop-control debugger-input"
|
|
334
312
|
data-prop="${prop}"
|
|
335
|
-
|
|
336
|
-
|
|
313
|
+
${value ? 'checked' : ''}
|
|
314
|
+
data-debugger-input="true">
|
|
315
|
+
<span class="checkbox-label">${value ? 'true' : 'false'}</span>
|
|
337
316
|
</div>
|
|
338
317
|
`;
|
|
339
|
-
} else if (
|
|
340
|
-
const checked = value ? 'checked' : '';
|
|
318
|
+
} else if (type === 'number') {
|
|
341
319
|
return `
|
|
342
|
-
<
|
|
343
|
-
<input type="
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
320
|
+
<div class="input-group">
|
|
321
|
+
<input type="number"
|
|
322
|
+
class="prop-control debugger-input"
|
|
323
|
+
data-prop="${prop}"
|
|
324
|
+
value="${serializedValue}"
|
|
325
|
+
step="any"
|
|
326
|
+
placeholder="Enter number..."
|
|
327
|
+
data-debugger-input="true">
|
|
328
|
+
</div>
|
|
329
|
+
`;
|
|
330
|
+
} else if (type === 'object' || type === 'array' || type === 'function') {
|
|
331
|
+
return `
|
|
332
|
+
<div class="input-group">
|
|
333
|
+
<input type="text"
|
|
334
|
+
class="prop-control debugger-input"
|
|
335
|
+
data-prop="${prop}"
|
|
336
|
+
value="${serializedValue}"
|
|
337
|
+
readonly
|
|
338
|
+
title="Click edit button to modify"
|
|
339
|
+
data-debugger-input="true">
|
|
340
|
+
<button class="edit-btn" onclick="slice.debugger.openAdvancedEditor('${prop}', '${type}')">✏️</button>
|
|
341
|
+
</div>
|
|
348
342
|
`;
|
|
349
343
|
} else {
|
|
350
|
-
// ✅ CORREGIDO: Inputs normales SIN readonly para permitir edición
|
|
351
344
|
return `
|
|
352
|
-
<
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
345
|
+
<div class="input-group">
|
|
346
|
+
<input type="text"
|
|
347
|
+
class="prop-control debugger-input"
|
|
348
|
+
data-prop="${prop}"
|
|
349
|
+
value="${serializedValue}"
|
|
350
|
+
placeholder="Enter value..."
|
|
351
|
+
data-debugger-input="true">
|
|
352
|
+
</div>
|
|
357
353
|
`;
|
|
358
354
|
}
|
|
359
355
|
}
|
|
360
356
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (
|
|
364
|
-
if (typeof value === 'object') return JSON.stringify(value);
|
|
365
|
-
return String(value);
|
|
366
|
-
}
|
|
357
|
+
applyPropertyChange(inputElement) {
|
|
358
|
+
const prop = inputElement.dataset.prop;
|
|
359
|
+
if (!prop) return;
|
|
367
360
|
|
|
368
|
-
|
|
369
|
-
const component = this.currentComponent;
|
|
361
|
+
let newValue;
|
|
370
362
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
363
|
+
if (inputElement.type === 'checkbox') {
|
|
364
|
+
newValue = inputElement.checked;
|
|
365
|
+
const label = inputElement.parentNode.querySelector('.checkbox-label');
|
|
366
|
+
if (label) {
|
|
367
|
+
label.textContent = newValue ? 'true' : 'false';
|
|
368
|
+
}
|
|
369
|
+
} else if (inputElement.type === 'number') {
|
|
370
|
+
newValue = Number(inputElement.value);
|
|
371
|
+
} else {
|
|
372
|
+
newValue = inputElement.value;
|
|
373
|
+
|
|
374
|
+
// Convert string values
|
|
375
|
+
if (newValue === 'true') newValue = true;
|
|
376
|
+
if (newValue === 'false') newValue = false;
|
|
377
|
+
if (!isNaN(newValue) && newValue !== '' && newValue !== null) newValue = Number(newValue);
|
|
378
|
+
}
|
|
379
379
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
380
|
+
const oldValue = this.currentComponent[prop];
|
|
381
|
+
|
|
382
|
+
this.currentComponent[prop] = newValue;
|
|
383
|
+
slice.logger.logInfo('Debugger', `Updated ${prop}: ${oldValue} → ${newValue}`);
|
|
384
|
+
|
|
385
|
+
this.showVisualFeedback(inputElement);
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
388
|
+
showVisualFeedback(inputElement) {
|
|
389
|
+
const originalBorder = inputElement.style.borderColor;
|
|
390
|
+
const originalBoxShadow = inputElement.style.boxShadow;
|
|
391
391
|
|
|
392
|
-
|
|
393
|
-
|
|
392
|
+
inputElement.style.borderColor = '#4CAF50';
|
|
393
|
+
inputElement.style.boxShadow = '0 0 0 2px rgba(76, 175, 80, 0.3)';
|
|
394
394
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
395
|
+
setTimeout(() => {
|
|
396
|
+
inputElement.style.borderColor = originalBorder;
|
|
397
|
+
inputElement.style.boxShadow = originalBoxShadow;
|
|
398
|
+
}, 1500);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
applyAllChanges() {
|
|
402
|
+
const inputs = this.querySelectorAll('.prop-control:not([readonly])');
|
|
403
|
+
let changeCount = 0;
|
|
404
|
+
|
|
405
|
+
inputs.forEach(input => {
|
|
406
|
+
if (!input.readOnly) {
|
|
407
|
+
this.applyPropertyChange(input);
|
|
408
|
+
changeCount++;
|
|
409
|
+
}
|
|
398
410
|
});
|
|
399
411
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
412
|
+
slice.logger.logInfo('Debugger', `Applied ${changeCount} property changes`);
|
|
413
|
+
this.showApplyFeedback(changeCount);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
showApplyFeedback(changeCount) {
|
|
417
|
+
const applyBtn = this.querySelector('#apply-changes');
|
|
418
|
+
if (!applyBtn) return;
|
|
419
|
+
|
|
420
|
+
const originalText = applyBtn.textContent;
|
|
421
|
+
|
|
422
|
+
if (changeCount > 0) {
|
|
423
|
+
applyBtn.textContent = `✅ Applied ${changeCount} changes!`;
|
|
424
|
+
applyBtn.style.background = '#4CAF50';
|
|
404
425
|
} else {
|
|
405
|
-
|
|
426
|
+
applyBtn.textContent = '✅ No changes to apply';
|
|
427
|
+
applyBtn.style.background = '#9E9E9E';
|
|
406
428
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
429
|
+
|
|
430
|
+
setTimeout(() => {
|
|
431
|
+
applyBtn.textContent = originalText;
|
|
432
|
+
applyBtn.style.background = '';
|
|
433
|
+
}, 2000);
|
|
411
434
|
}
|
|
412
435
|
|
|
413
|
-
|
|
436
|
+
openAdvancedEditor(prop, type) {
|
|
437
|
+
this.currentEditingProp = prop;
|
|
414
438
|
this.currentEditingType = type;
|
|
415
439
|
|
|
440
|
+
const value = this.currentComponent[prop];
|
|
441
|
+
|
|
442
|
+
this.modalTitle.textContent = `Edit ${prop} (${type})`;
|
|
443
|
+
|
|
416
444
|
this.querySelectorAll('.type-btn').forEach(btn => {
|
|
417
445
|
btn.classList.toggle('active', btn.dataset.type === type);
|
|
418
446
|
});
|
|
419
|
-
|
|
420
|
-
const value = this.componentProps[this.currentEditingProp];
|
|
421
447
|
|
|
422
448
|
if (type === 'function') {
|
|
423
|
-
|
|
424
|
-
value.toString()
|
|
449
|
+
if (typeof value === 'function') {
|
|
450
|
+
this.propertyEditor.value = value.toString();
|
|
451
|
+
} else {
|
|
452
|
+
this.propertyEditor.value = 'function() {\n // Your code here\n}';
|
|
453
|
+
}
|
|
425
454
|
} else {
|
|
426
455
|
this.propertyEditor.value = JSON.stringify(value, null, 2);
|
|
427
456
|
}
|
|
428
457
|
|
|
429
|
-
this.
|
|
458
|
+
this.editorModal.classList.add('active');
|
|
459
|
+
this.propertyEditor.focus();
|
|
430
460
|
}
|
|
431
461
|
|
|
432
462
|
validateEditor() {
|
|
433
|
-
const
|
|
434
|
-
const
|
|
463
|
+
const value = this.propertyEditor.value.trim();
|
|
464
|
+
const type = this.currentEditingType;
|
|
435
465
|
|
|
436
466
|
try {
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
if (content.trim().startsWith('function') || content.trim().startsWith('(') || content.includes('=>')) {
|
|
440
|
-
new Function('return ' + content);
|
|
441
|
-
this.validationMessage.textContent = '✅ Valid function syntax';
|
|
442
|
-
this.validationMessage.style.color = 'var(--success-color)';
|
|
443
|
-
saveBtn.disabled = false;
|
|
444
|
-
} else {
|
|
445
|
-
throw new Error('Invalid function syntax');
|
|
446
|
-
}
|
|
467
|
+
if (type === 'function') {
|
|
468
|
+
new Function('return ' + value)();
|
|
447
469
|
} else {
|
|
448
|
-
JSON.parse(
|
|
449
|
-
this.validationMessage.textContent = '✅ Valid JSON';
|
|
450
|
-
this.validationMessage.style.color = 'var(--success-color)';
|
|
451
|
-
saveBtn.disabled = false;
|
|
470
|
+
JSON.parse(value);
|
|
452
471
|
}
|
|
472
|
+
|
|
473
|
+
this.validationMessage.textContent = '✅ Valid syntax';
|
|
474
|
+
this.validationMessage.style.color = '#4CAF50';
|
|
475
|
+
this.querySelector('#modal-save').disabled = false;
|
|
453
476
|
} catch (error) {
|
|
454
477
|
this.validationMessage.textContent = `❌ ${error.message}`;
|
|
455
|
-
this.validationMessage.style.color = '
|
|
456
|
-
|
|
478
|
+
this.validationMessage.style.color = '#F44336';
|
|
479
|
+
this.querySelector('#modal-save').disabled = true;
|
|
457
480
|
}
|
|
458
481
|
}
|
|
459
482
|
|
|
460
483
|
savePropertyValue() {
|
|
461
|
-
const
|
|
484
|
+
const value = this.propertyEditor.value.trim();
|
|
485
|
+
const type = this.currentEditingType;
|
|
462
486
|
|
|
463
487
|
try {
|
|
464
488
|
let newValue;
|
|
465
489
|
|
|
466
|
-
if (
|
|
467
|
-
newValue = new Function('return ' +
|
|
490
|
+
if (type === 'function') {
|
|
491
|
+
newValue = new Function('return ' + value)();
|
|
468
492
|
} else {
|
|
469
|
-
newValue = JSON.parse(
|
|
493
|
+
newValue = JSON.parse(value);
|
|
470
494
|
}
|
|
471
|
-
|
|
472
|
-
// Update component property
|
|
473
|
-
this.currentComponent[this.currentEditingProp] = newValue;
|
|
474
|
-
this.componentProps[this.currentEditingProp] = newValue;
|
|
475
495
|
|
|
476
|
-
|
|
496
|
+
this.currentComponent[this.currentEditingProp] = newValue;
|
|
477
497
|
this.closeModal();
|
|
478
498
|
|
|
479
|
-
// ✅ Recrear props solo después de editar objeto/función (necesario)
|
|
480
|
-
setTimeout(() => {
|
|
481
|
-
this.updatePropsTab();
|
|
482
|
-
}, 50);
|
|
483
|
-
|
|
484
499
|
slice.logger.logInfo('Debugger', `Updated ${this.currentEditingProp} via advanced editor`);
|
|
500
|
+
|
|
501
|
+
const input = this.querySelector(`[data-prop="${this.currentEditingProp}"]`);
|
|
502
|
+
if (input) {
|
|
503
|
+
input.value = this.serializeValue(newValue);
|
|
504
|
+
this.showVisualFeedback(input);
|
|
505
|
+
}
|
|
506
|
+
|
|
485
507
|
} catch (error) {
|
|
486
508
|
this.validationMessage.textContent = `❌ ${error.message}`;
|
|
487
|
-
this.validationMessage.style.color = '
|
|
509
|
+
this.validationMessage.style.color = '#F44336';
|
|
488
510
|
}
|
|
489
511
|
}
|
|
490
512
|
|
|
@@ -495,121 +517,96 @@ export default class Debugger extends HTMLElement {
|
|
|
495
517
|
this.validationMessage.textContent = '';
|
|
496
518
|
}
|
|
497
519
|
|
|
498
|
-
// ✅ NUEVO: Aplicar cambio de una propiedad específica SIN tocar UI
|
|
499
|
-
applyPropertyChange(inputElement) {
|
|
500
|
-
const prop = inputElement.dataset.prop;
|
|
501
|
-
if (!prop) return;
|
|
502
|
-
|
|
503
|
-
let newValue;
|
|
504
|
-
|
|
505
|
-
if (inputElement.type === 'checkbox') {
|
|
506
|
-
newValue = inputElement.checked;
|
|
507
|
-
} else if (inputElement.type === 'number') {
|
|
508
|
-
newValue = Number(inputElement.value);
|
|
509
|
-
} else {
|
|
510
|
-
newValue = inputElement.value;
|
|
511
|
-
|
|
512
|
-
// Convert string values
|
|
513
|
-
if (newValue === 'true') newValue = true;
|
|
514
|
-
if (newValue === 'false') newValue = false;
|
|
515
|
-
if (!isNaN(newValue) && newValue !== '') newValue = Number(newValue);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
const oldValue = this.currentComponent[prop];
|
|
519
|
-
|
|
520
|
-
if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
|
521
|
-
this.currentComponent[prop] = newValue;
|
|
522
|
-
slice.logger.logInfo('Debugger', `Updated ${prop}: ${oldValue} → ${newValue}`);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// ✅ SIMPLIFICADO: Solo para el botón Apply Changes
|
|
527
|
-
applyAllChanges() {
|
|
528
|
-
const inputs = this.propsContainer.querySelectorAll('.prop-control');
|
|
529
|
-
let changesCount = 0;
|
|
530
|
-
|
|
531
|
-
inputs.forEach(input => {
|
|
532
|
-
const prop = input.dataset.prop;
|
|
533
|
-
if (!prop) return;
|
|
534
|
-
|
|
535
|
-
let newValue;
|
|
536
|
-
|
|
537
|
-
if (input.type === 'checkbox') {
|
|
538
|
-
newValue = input.checked;
|
|
539
|
-
} else if (input.type === 'number') {
|
|
540
|
-
newValue = Number(input.value);
|
|
541
|
-
} else {
|
|
542
|
-
newValue = input.value;
|
|
543
|
-
|
|
544
|
-
if (newValue === 'true') newValue = true;
|
|
545
|
-
if (newValue === 'false') newValue = false;
|
|
546
|
-
if (!isNaN(newValue) && newValue !== '') newValue = Number(newValue);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const oldValue = this.currentComponent[prop];
|
|
550
|
-
|
|
551
|
-
if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
|
552
|
-
this.currentComponent[prop] = newValue;
|
|
553
|
-
changesCount++;
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
slice.logger.logInfo('Debugger', `Applied ${changesCount} changes via button`);
|
|
558
|
-
|
|
559
|
-
if (changesCount > 0) {
|
|
560
|
-
this.showApplyFeedback(changesCount);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// ✅ NUEVO: Mostrar feedback visual
|
|
565
|
-
showApplyFeedback(changesCount) {
|
|
566
|
-
const applyBtn = this.querySelector('#apply-changes');
|
|
567
|
-
const originalText = applyBtn.textContent;
|
|
568
|
-
|
|
569
|
-
applyBtn.textContent = `✅ Applied ${changesCount} changes!`;
|
|
570
|
-
applyBtn.style.background = 'var(--success-color)';
|
|
571
|
-
|
|
572
|
-
setTimeout(() => {
|
|
573
|
-
applyBtn.textContent = originalText;
|
|
574
|
-
applyBtn.style.background = 'var(--primary-color)';
|
|
575
|
-
}, 1500);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
520
|
resetValues() {
|
|
579
521
|
const ComponentClass = this.currentComponent.constructor;
|
|
580
|
-
const
|
|
522
|
+
const configuredProps = ComponentClass.props || {};
|
|
581
523
|
let resetCount = 0;
|
|
582
524
|
|
|
583
|
-
Object.entries(
|
|
525
|
+
Object.entries(configuredProps).forEach(([prop, config]) => {
|
|
584
526
|
if (config.default !== undefined) {
|
|
585
527
|
this.currentComponent[prop] = config.default;
|
|
586
|
-
|
|
528
|
+
|
|
529
|
+
const input = this.querySelector(`[data-prop="${prop}"]`);
|
|
530
|
+
if (input && !input.readOnly) {
|
|
531
|
+
if (input.type === 'checkbox') {
|
|
532
|
+
input.checked = config.default;
|
|
533
|
+
const label = input.parentNode.querySelector('.checkbox-label');
|
|
534
|
+
if (label) {
|
|
535
|
+
label.textContent = config.default ? 'true' : 'false';
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
input.value = this.serializeValue(config.default);
|
|
539
|
+
}
|
|
540
|
+
resetCount++;
|
|
541
|
+
}
|
|
587
542
|
}
|
|
588
543
|
});
|
|
589
544
|
|
|
590
545
|
slice.logger.logInfo('Debugger', 'Reset values to defaults');
|
|
591
|
-
|
|
592
|
-
// ✅ CORREGIDO: Solo recrear UI después del reset (necesario)
|
|
593
|
-
if (resetCount > 0) {
|
|
594
|
-
this.updatePropsTab(); // Necesario para mostrar los nuevos valores
|
|
595
|
-
this.showResetFeedback(resetCount);
|
|
596
|
-
}
|
|
546
|
+
this.showResetFeedback(resetCount);
|
|
597
547
|
}
|
|
598
548
|
|
|
599
|
-
// ✅ NUEVO: Mostrar feedback visual para reset
|
|
600
549
|
showResetFeedback(resetCount) {
|
|
601
550
|
const resetBtn = this.querySelector('#reset-values');
|
|
551
|
+
if (!resetBtn) return;
|
|
552
|
+
|
|
602
553
|
const originalText = resetBtn.textContent;
|
|
603
554
|
|
|
604
555
|
resetBtn.textContent = `🔄 Reset ${resetCount} values!`;
|
|
605
|
-
resetBtn.style.background = '
|
|
556
|
+
resetBtn.style.background = '#FF9800';
|
|
606
557
|
|
|
607
558
|
setTimeout(() => {
|
|
608
559
|
resetBtn.textContent = originalText;
|
|
609
|
-
resetBtn.style.background = '
|
|
560
|
+
resetBtn.style.background = '';
|
|
610
561
|
}, 1500);
|
|
611
562
|
}
|
|
612
563
|
|
|
564
|
+
updateInfoTab() {
|
|
565
|
+
const infoContainer = this.querySelector('.info-list');
|
|
566
|
+
if (!infoContainer) return;
|
|
567
|
+
|
|
568
|
+
const component = this.currentComponent;
|
|
569
|
+
|
|
570
|
+
const info = [
|
|
571
|
+
{ label: 'Component Type', value: component.constructor.name },
|
|
572
|
+
{ label: 'Slice ID', value: component.sliceId || 'Not assigned' },
|
|
573
|
+
{ label: 'Tag Name', value: component.tagName },
|
|
574
|
+
{ label: 'Connected', value: component.isConnected ? 'Yes' : 'No' },
|
|
575
|
+
{ label: 'Props Count', value: Object.keys(this.componentProps).length },
|
|
576
|
+
{ label: 'Children', value: component.children.length }
|
|
577
|
+
];
|
|
578
|
+
|
|
579
|
+
infoContainer.innerHTML = info.map(item => `
|
|
580
|
+
<div class="info-item">
|
|
581
|
+
<span class="info-label">${item.label}</span>
|
|
582
|
+
<span class="info-value">${item.value}</span>
|
|
583
|
+
</div>
|
|
584
|
+
`).join('');
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
getValueType(value) {
|
|
588
|
+
if (value === null) return 'null';
|
|
589
|
+
if (value === undefined) return 'undefined';
|
|
590
|
+
if (Array.isArray(value)) return 'array';
|
|
591
|
+
return typeof value;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
serializeValue(value) {
|
|
595
|
+
if (value === null || value === undefined) {
|
|
596
|
+
return '';
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
600
|
+
try {
|
|
601
|
+
return JSON.stringify(value);
|
|
602
|
+
} catch {
|
|
603
|
+
return String(value);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return String(value);
|
|
608
|
+
}
|
|
609
|
+
|
|
613
610
|
getComponentPropsForDebugger(component) {
|
|
614
611
|
const ComponentClass = component.constructor;
|
|
615
612
|
|