@ingcrea/aumic-tailwind-killer 3.1.7 → 4.1.4
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.
- package/.npmrc.bak +1 -0
- package/LICENSE +661 -0
- package/LICENSE.es.md +38 -0
- package/README.es.md +382 -0
- package/README.md +424 -108
- package/aumic-lexicon.duckdb +0 -0
- package/dist/ast/parser.js +11 -11
- package/dist/index.js +853 -685
- package/package.json +51 -48
- package/patch.js +0 -80
package/dist/index.js
CHANGED
|
@@ -21,9 +21,10 @@ const parser_1 = require("./ast/parser");
|
|
|
21
21
|
const provider_1 = require("./ai/provider");
|
|
22
22
|
const scraper_1 = require("./core/scraper");
|
|
23
23
|
// Versión dinámica
|
|
24
|
-
let
|
|
24
|
+
let userConfig = {};
|
|
25
|
+
let pkgVersion = '4.1.3';
|
|
25
26
|
try {
|
|
26
|
-
pkgVersion = JSON.parse(fs_extra_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf-8')).version;
|
|
27
|
+
pkgVersion = JSON.parse(fs_extra_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf-8').replace(/^\uFEFF/, '')).version;
|
|
27
28
|
}
|
|
28
29
|
catch (e) { }
|
|
29
30
|
async function runConcurrent(items, concurrency, task) {
|
|
@@ -117,11 +118,11 @@ function generateReport(mapping, targetDir, isSimulate = true) {
|
|
|
117
118
|
if (counts[cat] !== undefined)
|
|
118
119
|
counts[cat]++;
|
|
119
120
|
const c = categoryMeta[cat] || categoryMeta.molecules;
|
|
120
|
-
rows.push(`<tr data-cat="${cat}">
|
|
121
|
-
<td class="tw-cell copyable" onclick="copyText(this)">${orig}</td>
|
|
122
|
-
<td class="aumic-cell copyable" onclick="copyText(this)">${map.aumicClass}</td>
|
|
123
|
-
<td class="occ-cell" data-occ="${occ}"><div class="occ-bar" style="width: ${Math.min(100, occ * 2)}%; background: ${c.text}"></div><span>${occ}</span></td>
|
|
124
|
-
<td><span class="badge" style="border-color:${c.border};color:${c.text};background:${c.bg}">${c.label}</span></td>
|
|
121
|
+
rows.push(`<tr data-cat="${cat}">
|
|
122
|
+
<td class="tw-cell copyable" onclick="copyText(this)">${orig}</td>
|
|
123
|
+
<td class="aumic-cell copyable" onclick="copyText(this)">${map.aumicClass}</td>
|
|
124
|
+
<td class="occ-cell" data-occ="${occ}"><div class="occ-bar" style="width: ${Math.min(100, occ * 2)}%; background: ${c.text}"></div><span>${occ}</span></td>
|
|
125
|
+
<td><span class="badge" style="border-color:${c.border};color:${c.text};background:${c.bg}">${c.label}</span></td>
|
|
125
126
|
</tr>`);
|
|
126
127
|
}
|
|
127
128
|
const savedBytes = originalBytes - aumicBytes;
|
|
@@ -155,675 +156,675 @@ function generateReport(mapping, targetDir, isSimulate = true) {
|
|
|
155
156
|
{ label: 'Galaxias', value: counts.galaxies, color: '#facc15' }
|
|
156
157
|
];
|
|
157
158
|
// Top 5 Criminals
|
|
158
|
-
const top5 = sortedMappings.slice(0, 5).map((m, i) => `
|
|
159
|
-
<div class="criminal">
|
|
160
|
-
<div class="c-rank">#${i + 1}</div>
|
|
161
|
-
<div class="c-info">
|
|
162
|
-
<div class="c-class">${m[0]}</div>
|
|
163
|
-
<div class="c-stats">Repeticiones: <strong>${m[1].occurrences || 1}</strong> | Categoría: ${m[1].category}</div>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
159
|
+
const top5 = sortedMappings.slice(0, 5).map((m, i) => `
|
|
160
|
+
<div class="criminal">
|
|
161
|
+
<div class="c-rank">#${i + 1}</div>
|
|
162
|
+
<div class="c-info">
|
|
163
|
+
<div class="c-class">${m[0]}</div>
|
|
164
|
+
<div class="c-stats">Repeticiones: <strong>${m[1].occurrences || 1}</strong> | Categoría: ${m[1].category}</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
166
167
|
`).join('');
|
|
167
|
-
const html = `<!DOCTYPE html>
|
|
168
|
-
<html lang="es"><head><meta charset="UTF-8"><title>AUM-IC · Dashboard C4I</title><style>
|
|
169
|
-
:root { --bg: #030712; --panel: rgba(15, 23, 42, 0.7); --panel-hover: #1e293b; --text: #e2e8f0; --muted: #64748b; --border: rgba(51, 65, 85, 0.6); --accent: #00e5ff; }
|
|
170
|
-
*{box-sizing:border-box;margin:0;padding:0}
|
|
171
|
-
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;padding:2rem 3rem;position:relative;overflow-x:hidden;}
|
|
172
|
-
#bgCanvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
|
|
173
|
-
.content-wrapper { position: relative; z-index: 10; }
|
|
174
|
-
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid var(--border)}
|
|
175
|
-
header h1{font-size:1.8rem;font-weight:900;color:#fff;letter-spacing:-0.5px}header h1 span{color:var(--accent);text-shadow:0 0 10px rgba(0,229,255,0.4)}
|
|
176
|
-
.subtitle{font-size:0.85rem;color:var(--muted);margin-top:6px}.safe-tag{background:rgba(0,229,255,0.1);border:1px solid rgba(0,229,255,0.3);color:var(--accent);font-size:0.75rem;padding:6px 16px;border-radius:999px;font-weight:700;box-shadow:0 0 15px rgba(0,229,255,0.1)}
|
|
177
|
-
.dashboard-grid { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; }
|
|
178
|
-
.sidebar { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
179
|
-
.main-view { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
180
|
-
|
|
181
|
-
/* Panels */
|
|
182
|
-
.panel { background: var(--panel); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); padding: 1.5rem; border-radius: 16px; border: 1px solid var(--border); box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
|
|
183
|
-
.panel h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 1rem; }
|
|
184
|
-
|
|
185
|
-
/* Canvas Chart */
|
|
186
|
-
.chart-container { display: flex; justify-content: center; align-items: center; margin-bottom: 1rem; position: relative; height: 200px; }
|
|
187
|
-
.chart-center-overlay { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; pointer-events: none; }
|
|
188
|
-
.chart-center-overlay .score { font-size: 2.5rem; font-weight: 900; color: ${gradeColor}; line-height: 1; text-shadow: 0 0 15px ${gradeColor}44; }
|
|
189
|
-
.chart-center-overlay .score-lbl { font-size: 0.6rem; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-top: 4px; }
|
|
190
|
-
.legend { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 0.75rem; }
|
|
191
|
-
.leg-item { display: flex; align-items: center; gap: 6px; }
|
|
192
|
-
.leg-color { width: 10px; height: 10px; border-radius: 50%; }
|
|
193
|
-
|
|
194
|
-
/* Hero Metrics */
|
|
195
|
-
.metrics-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
|
|
196
|
-
.metric { background: rgba(0,0,0,0.3); padding: 1.2rem; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); backdrop-filter: blur(5px); }
|
|
197
|
-
.metric h3 { font-size: 1.8rem; font-weight: 900; color: #fff; line-height: 1; margin-bottom: 4px; }
|
|
198
|
-
.metric p { font-size: 0.7rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
199
|
-
.highlight { color: #34d399 !important; text-shadow: 0 0 10px rgba(52,211,153,0.4); }
|
|
200
|
-
.highlight-psi { color: #facc15 !important; }
|
|
201
|
-
|
|
202
|
-
/* Criminals */
|
|
203
|
-
.criminal { display: flex; gap: 1rem; align-items: center; padding: 10px; background: rgba(0,0,0,0.2); border-radius: 8px; margin-bottom: 8px; }
|
|
204
|
-
.c-rank { font-size: 1.2rem; font-weight: 900; color: #ef4444; width: 30px; text-align: center; }
|
|
205
|
-
.c-info { flex: 1; min-width: 0; }
|
|
206
|
-
.c-class { font-family: 'Fira Code', monospace; font-size: 0.75rem; color: #f472b6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; }
|
|
207
|
-
.c-stats { font-size: 0.65rem; color: var(--muted); }
|
|
208
|
-
|
|
209
|
-
/* Table Controls */
|
|
210
|
-
.toolbar{display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap; margin-bottom: 1.5rem;}
|
|
211
|
-
.fbtn{background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#94a3b8;font-size:0.75rem;padding:8px 16px;border-radius:8px;cursor:pointer;transition:all 0.2s;font-weight:500}
|
|
212
|
-
.fbtn:hover,.fbtn.active{color:#fff;border-color:var(--accent);background:rgba(0,229,255,0.1);box-shadow:0 0 10px rgba(0,229,255,0.2)}
|
|
213
|
-
.search{margin-left:auto;background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.8rem;padding:8px 16px;border-radius:8px;outline:none;width:220px;transition:all 0.2s}
|
|
214
|
-
.search:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,229,255,0.2)}
|
|
215
|
-
.page-size-sel { background: rgba(15, 23, 42, 0.9); color: #fff; background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.75rem;padding:8px;border-radius:8px;outline:none; cursor:pointer;}
|
|
216
|
-
|
|
217
|
-
/* Table */
|
|
218
|
-
.table-container{border-radius:12px;border:1px solid var(--border);overflow:hidden;background:rgba(0,0,0,0.2);}
|
|
219
|
-
table{width:100%;border-collapse:collapse;font-size:0.8rem}
|
|
220
|
-
th{background:rgba(0,0,0,0.3);color:var(--muted);text-align:left;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;text-transform:uppercase;letter-spacing:1px;font-size:0.7rem;cursor:pointer;user-select:none}
|
|
221
|
-
th:hover{color:#fff}
|
|
222
|
-
td{padding:12px 16px;border-bottom:1px solid rgba(255,255,255,0.02);vertical-align:middle;transition:background 0.15s}
|
|
223
|
-
tr:hover td{background:rgba(255,255,255,0.05)}
|
|
224
|
-
.tw-cell{font-family:'Fira Code',Consolas,monospace;color:#f472b6;font-size:0.75rem;max-width:300px;word-break:break-all}
|
|
225
|
-
.aumic-cell{font-family:'Fira Code',Consolas,monospace;color:#38bdf8;font-size:0.75rem;white-space:nowrap;font-weight:600}
|
|
226
|
-
.occ-cell{display:flex;align-items:center;gap:10px;font-family:'Fira Code',monospace;font-weight:bold;color:#fff}
|
|
227
|
-
.occ-bar{height:6px;border-radius:3px;opacity:0.8}
|
|
228
|
-
.badge{font-size:0.65rem;padding:4px 10px;border-radius:999px;border:1px solid;font-weight:800;white-space:nowrap;text-transform:uppercase;letter-spacing:0.5px}
|
|
229
|
-
|
|
230
|
-
/* Pagination */
|
|
231
|
-
.pagination { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: rgba(0,0,0,0.3); border-top: 1px solid var(--border); }
|
|
232
|
-
.pag-controls { display: flex; gap: 6px; align-items: center; background: rgba(15,23,42,0.6); padding: 4px; border-radius: 8px; border: 1px solid var(--border); }
|
|
233
|
-
.pag-btn { background: transparent; border: none; color: var(--muted); padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; transition: 0.2s; min-width: 32px; text-align: center; font-weight: 600; }
|
|
234
|
-
.pag-btn:hover:not(:disabled) { background: rgba(255,255,255,0.05); color: #fff; }
|
|
235
|
-
.pag-btn.active { background: rgba(0,229,255,0.15); color: var(--accent); box-shadow: inset 0 0 0 1px rgba(0,229,255,0.3); }
|
|
236
|
-
.pag-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
237
|
-
.pag-info { font-size: 0.75rem; color: var(--muted); }
|
|
238
|
-
.pag-dots { color: var(--muted); font-size: 0.75rem; font-weight: bold; padding: 0 4px; }
|
|
239
|
-
|
|
240
|
-
/* Footer */
|
|
241
|
-
.footer { position: relative; bottom: 0; left: 0; width: 100%; text-align: center; padding: 20px; font-size: 0.75rem; color: var(--muted); border-top: 1px solid rgba(255,255,255,0.05); background: rgba(0,0,0,0.3); backdrop-filter: blur(5px); margin-top: auto; }
|
|
242
|
-
.footer a { color: var(--accent); text-decoration: none; font-weight: 600; transition: color 0.2s; }
|
|
243
|
-
.footer a:hover { color: #fff; text-shadow: 0 0 10px var(--accent); }
|
|
244
|
-
.page-size-sel option { background: #0f172a; color: #fff; }
|
|
245
|
-
/* Export & Toast */
|
|
246
|
-
.export-group-top { display: flex; gap: 8px; margin-left: auto; align-items: center; } .export-group-top .fbtn { background: rgba(0,229,255,0.05); }
|
|
247
|
-
.toast { position: fixed; bottom: 20px; right: 20px; background: rgba(0, 229, 255, 0.15); border: 1px solid var(--accent); color: #fff; padding: 10px 20px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; box-shadow: 0 5px 15px rgba(0,229,255,0.2); backdrop-filter: blur(5px); transform: translateY(100px); opacity: 0; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 9999; }
|
|
248
|
-
.toast.show { transform: translateY(0); opacity: 1; }
|
|
249
|
-
.copyable { cursor: pointer; transition: color 0.2s; position: relative; }
|
|
250
|
-
.copyable:hover { color: #fff; text-shadow: 0 0 8px rgba(255,255,255,0.5); }
|
|
251
|
-
.copyable::after { content: 'Copiar'; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); color: #fff; font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; opacity: 0; pointer-events: none; transition: opacity 0.2s; }
|
|
252
|
-
.copyable:hover::after { opacity: 1; }
|
|
253
|
-
|
|
254
|
-
/* Sticky Header */
|
|
255
|
-
.table-container { overflow-x: auto; }
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
/* Print PDF (Mesa Directiva) */
|
|
259
|
-
@media print {
|
|
260
|
-
body { background: #fff !important; color: #000 !important; padding: 0 !important; font-size: 10pt; }
|
|
261
|
-
#bgCanvas, .toolbar, .pagination, .safe-tag, .export-group-top, .footer, .toast { display: none !important; }
|
|
262
|
-
.content-wrapper { padding: 0 !important; max-width: 100% !important; }
|
|
263
|
-
header { border-bottom: 2px solid #000; padding-bottom: 10px; margin-bottom: 20px; flex-direction: column !important; align-items: flex-start !important; }
|
|
264
|
-
header h1 { color: #000 !important; text-shadow: none !important; margin: 0; }
|
|
265
|
-
header h1 span { color: #000 !important; }
|
|
266
|
-
|
|
267
|
-
.dashboard-grid { display: block !important; }
|
|
268
|
-
.sidebar { display: flex !important; flex-direction: row !important; gap: 20px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
269
|
-
.sidebar .panel { flex: 1; border: 1px solid #cbd5e1 !important; padding: 15px !important; border-radius: 8px !important; }
|
|
270
|
-
|
|
271
|
-
.panel { background: none !important; border: none !important; box-shadow: none !important; padding: 0 !important; }
|
|
272
|
-
|
|
273
|
-
.metrics-grid { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 10px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
274
|
-
.metric { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; padding: 10px !important; border-radius: 8px !important; }
|
|
275
|
-
.metric h3 { color: #000 !important; text-shadow: none !important; font-size: 1.2rem; margin-bottom: 5px; }
|
|
276
|
-
.metric p { color: #333 !important; font-size: 0.8rem; }
|
|
277
|
-
|
|
278
|
-
.criteria-box { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; color: #000 !important; }
|
|
279
|
-
.criteria-box strong { color: #000 !important; }
|
|
280
|
-
.criteria-box span { color: #000 !important; font-weight: bold; }
|
|
281
|
-
|
|
282
|
-
.chart-center-overlay .score { color: #000 !important; text-shadow: none !important; }
|
|
283
|
-
.chart-center-overlay .score-lbl { color: #333 !important; }
|
|
284
|
-
|
|
285
|
-
.leg-item { color: #000 !important; }
|
|
286
|
-
|
|
287
|
-
.table-container { max-height: none !important; border: none !important; overflow: visible !important; }
|
|
288
|
-
table { border-collapse: collapse !important; width: 100% !important; page-break-inside: auto; }
|
|
289
|
-
tr { page-break-inside: avoid; page-break-after: auto; }
|
|
290
|
-
th, td { border: 1px solid #cbd5e1 !important; color: #000 !important; padding: 6px !important; font-size: 9pt !important; }
|
|
291
|
-
th { background: #f1f5f9 !important; color: #000 !important; font-weight: bold; }
|
|
292
|
-
.tw-cell, .aumic-cell { color: #000 !important; font-weight: normal; }
|
|
293
|
-
.occ-bar { background: #94a3b8 !important; }
|
|
294
|
-
|
|
295
|
-
@page { margin: 1cm; size: auto; }
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
</style></head><body style="display: flex; flex-direction: column;">
|
|
299
|
-
|
|
300
|
-
<canvas id="bgCanvas"></canvas>
|
|
301
|
-
|
|
302
|
-
<div class="content-wrapper">
|
|
303
|
-
<header>
|
|
304
|
-
<div><h1>⚔️ <span>AUM-IC</span> · Centro de Inteligencia</h1>
|
|
305
|
-
<p class="subtitle">Análisis Forense completado exitosamente sin mutación en disco.</p></div>
|
|
306
|
-
<div class="export-group-top" style="display:flex; gap:8px; margin-left: 20px;">
|
|
307
|
-
<button class="fbtn" onclick="exportCSV()">⬇️ CSV</button><button class="fbtn" onclick="exportMD()">⬇️ MD</button><button class="fbtn" onclick="window.print()">🖨️ PDF</button>
|
|
308
|
-
</div>
|
|
309
|
-
<div class="safe-tag" style="align-items: flex-end; display: flex; flex-direction: column;">
|
|
310
|
-
<span>${isSimulate ? 'MODO DRY-RUN ACTIVO' : 'MUTACIÓN AUM-IC COMPLETADA'}</span>
|
|
311
|
-
<span style="font-size: 0.65rem; opacity: 0.8; margin-top: 2px;">v${pkgVersion}</span>
|
|
312
|
-
</div>
|
|
313
|
-
</header>
|
|
314
|
-
|
|
315
|
-
<main class="dashboard-grid">
|
|
316
|
-
<aside class="sidebar">
|
|
317
|
-
<article class="panel">
|
|
318
|
-
<h2>Mapeo Estructural</h2>
|
|
319
|
-
<div class="chart-container">
|
|
320
|
-
<canvas id="donutCanvas" width="220" height="220"></canvas>
|
|
321
|
-
<div class="chart-center-overlay">
|
|
322
|
-
<div class="score">${grade}</div>
|
|
323
|
-
<div class="score-lbl">Rank</div>
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
<div class="legend">
|
|
327
|
-
<div class="leg-item"><div class="leg-color" style="background:#22d3ee"></div>Átomos (${counts.atoms})</div>
|
|
328
|
-
<div class="leg-item"><div class="leg-color" style="background:#a78bfa"></div>Moléculas (${counts.molecules})</div>
|
|
329
|
-
<div class="leg-item"><div class="leg-color" style="background:#34d399"></div>Organismos (${counts.organisms})</div>
|
|
330
|
-
<div class="leg-item"><div class="leg-color" style="background:#fb923c"></div>Ecosistemas (${counts.ecosystems})</div>
|
|
331
|
-
<div class="leg-item"><div class="leg-color" style="background:#facc15"></div>Galaxias (${counts.galaxies})</div>
|
|
332
|
-
</div>
|
|
333
|
-
<div class="criteria-box" style="margin-top: 1.5rem; padding: 1rem; background: rgba(0,0,0,0.2); border-radius: 8px; border: 1px solid var(--border); font-size: 0.75rem; color: var(--muted); line-height: 1.5;">
|
|
334
|
-
<strong style="color: #fff; margin-bottom: 0.5rem; display: block;">Criterio de Evaluación AUM-IC</strong>
|
|
335
|
-
<ul style="margin: 0; padding-left: 1rem; list-style: square;">
|
|
336
|
-
<li><span style="color:#22d3ee">Átomos:</span> Utilidades base simples (p-4, m-2, flex).</li>
|
|
337
|
-
<li><span style="color:#a78bfa">Moléculas:</span> Estilos condicionales y variantes (hover:, focus:).</li>
|
|
338
|
-
<li><span style="color:#34d399">Organismos:</span> Cadenas largas y componentes estructurales.</li>
|
|
339
|
-
<li><span style="color:#fb923c">Ecosistemas:</span> Combinaciones de Layouts complejos (Grid/Flex).</li>
|
|
340
|
-
<li><span style="color:#facc15">Galaxias:</span> Declaraciones globales masivas o de alta densidad.</li>
|
|
341
|
-
</ul>
|
|
342
|
-
<div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid rgba(255,255,255,0.1);">
|
|
343
|
-
<strong style="color: #fff; display:block; margin-bottom: 0.3rem;">🏆 Rank (Score) & Eficiencia</strong>
|
|
344
|
-
<div style="margin-bottom: 0.6rem; color: #94a3b8;">La <b>Eficiencia de Refactorización</b> mide el porcentaje exacto de peso (bytes) que AUM-IC logró purgar del DOM frente al HTML inflado original.</div>
|
|
345
|
-
<div style="display: flex; gap: 6px; flex-wrap: wrap; font-size: 0.7rem;">
|
|
346
|
-
<span style="background: rgba(250,204,21,0.15); color:#facc15; padding: 3px 6px; border-radius: 4px; border: 1px solid #facc1544;"><b>S</b> ≥ 70%</span>
|
|
347
|
-
<span style="background: rgba(52,211,153,0.15); color:#34d399; padding: 3px 6px; border-radius: 4px; border: 1px solid #34d39944;"><b>A</b> ≥ 50%</span>
|
|
348
|
-
<span style="background: rgba(34,211,238,0.15); color:#22d3ee; padding: 3px 6px; border-radius: 4px; border: 1px solid #22d3ee44;"><b>B</b> ≥ 30%</span>
|
|
349
|
-
<span style="background: rgba(167,139,250,0.15); color:#a78bfa; padding: 3px 6px; border-radius: 4px; border: 1px solid #a78bfa44;"><b>C</b> ≥ 15%</span>
|
|
350
|
-
<span style="background: rgba(239,68,68,0.15); color:#ef4444; padding: 3px 6px; border-radius: 4px; border: 1px solid #ef444444;"><b>D</b> < 15%</span>
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
</article>
|
|
355
|
-
|
|
356
|
-
<article class="panel">
|
|
357
|
-
<h2>☠️ Top 5 Ofensores del DOM</h2>
|
|
358
|
-
${top5}
|
|
359
|
-
</article>
|
|
360
|
-
</aside>
|
|
361
|
-
|
|
362
|
-
<section class="main-view">
|
|
363
|
-
<div class="metrics-grid">
|
|
364
|
-
<div class="metric">
|
|
365
|
-
<h3 class="highlight">🔥 ${savedKB} KB</h3>
|
|
366
|
-
<p>Peso/Tamaño Estimado Salvado</p>
|
|
367
|
-
</div>
|
|
368
|
-
<div class="metric">
|
|
369
|
-
<h3 class="highlight-psi">🚀 +${psiImpact} pts</h3>
|
|
370
|
-
<p>Impacto Estimado Google PSI</p>
|
|
371
|
-
</div>
|
|
372
|
-
<div class="metric">
|
|
373
|
-
<h3>${efficiency}%</h3>
|
|
374
|
-
<p>Eficiencia de Refactorización</p>
|
|
375
|
-
</div>
|
|
376
|
-
<div class="metric">
|
|
377
|
-
<h3>${mapping.size}</h3>
|
|
378
|
-
<p>Clases Únicas Tailwind</p>
|
|
379
|
-
</div>
|
|
380
|
-
<div class="metric">
|
|
381
|
-
<h3>${totalOccurrences}</h3>
|
|
382
|
-
<p>Instancias Totales Detectadas</p>
|
|
383
|
-
</div>
|
|
384
|
-
<div class="metric">
|
|
385
|
-
<h3>${totalOccurrences - mapping.size}</h3>
|
|
386
|
-
<p>Redundancia Estructural (Repetidas)</p>
|
|
387
|
-
</div>
|
|
388
|
-
</div>
|
|
389
|
-
|
|
390
|
-
<article class="panel">
|
|
391
|
-
<nav class="toolbar">
|
|
392
|
-
<button class="fbtn active" onclick="filter('all',this)">Todos</button>
|
|
393
|
-
<button class="fbtn" onclick="filter('atoms',this)">Átomos</button>
|
|
394
|
-
<button class="fbtn" onclick="filter('molecules',this)">Moléculas</button>
|
|
395
|
-
<button class="fbtn" onclick="filter('organisms',this)">Organismos</button>
|
|
396
|
-
<button class="fbtn" onclick="filter('ecosystems',this)">Ecosistemas</button>
|
|
397
|
-
<button class="fbtn" onclick="filter('galaxies',this)">Galaxias</button>
|
|
398
|
-
|
|
399
|
-
<input class="search" type="text" id="searchInput" placeholder="Buscar clase (Regex)..." oninput="search(this.value)">
|
|
400
|
-
<select class="page-size-sel" id="pageSizeSelect" onchange="changePageSize(this.value)">
|
|
401
|
-
<option value="20">20 por pág</option>
|
|
402
|
-
<option value="50">50 por pág</option>
|
|
403
|
-
<option value="100">100 por pág</option>
|
|
404
|
-
<option value="all">TODOS</option>
|
|
405
|
-
</select>
|
|
406
|
-
</nav>
|
|
407
|
-
<section class="table-container">
|
|
408
|
-
<table id="table">
|
|
409
|
-
<thead><tr>
|
|
410
|
-
<th onclick="sortTable(0, 'str')">Clase Original (Tailwind) ↕</th>
|
|
411
|
-
<th onclick="sortTable(1, 'str')">Clase AUM-IC Transmutada ↕</th>
|
|
412
|
-
<th onclick="sortTable(2, 'num')">Frecuencia ↕</th>
|
|
413
|
-
<th onclick="sortTable(3, 'str')">Nivel ↕</th>
|
|
414
|
-
</tr></thead>
|
|
415
|
-
<tbody id="tbody">${rows.join('')}</tbody>
|
|
416
|
-
</table>
|
|
417
|
-
<div class="pagination">
|
|
418
|
-
<span class="pag-info" id="page-info"></span>
|
|
419
|
-
<div class="pag-controls" id="pag-controls"></div>
|
|
420
|
-
</div>
|
|
421
|
-
</section>
|
|
422
|
-
</article>
|
|
423
|
-
</section>
|
|
424
|
-
</main>
|
|
425
|
-
</div>
|
|
426
|
-
|
|
427
|
-
<div id="toast" class="toast">✔ Copiado al portapapeles</div>
|
|
428
|
-
<footer class="footer">
|
|
429
|
-
Forged by <a href="https://ingcrea.com" target="_blank">INGENIERÍA CREATIVA Y DESARROLLOS TECNOLÓGICOS S.A.S.</a> |
|
|
430
|
-
<a href="https://github.com/ingcrea/aumic-tailwind-killer" target="_blank">View on GitHub</a>
|
|
431
|
-
</footer>
|
|
432
|
-
|
|
433
|
-
<script>
|
|
434
|
-
// --- MOTOR CANVAS: DONUT CHART ANIMADO ---
|
|
435
|
-
const chartData = ${JSON.stringify(chartData)};
|
|
436
|
-
const ctxChart = document.getElementById('donutCanvas').getContext('2d');
|
|
437
|
-
let totalVal = chartData.reduce((acc, d) => acc + d.value, 0);
|
|
438
|
-
if(totalVal === 0) totalVal = 1;
|
|
439
|
-
let animationProgress = 0;
|
|
440
|
-
let hoveredIndex = -1;
|
|
441
|
-
let mousePos = {x: 0, y: 0};
|
|
442
|
-
|
|
443
|
-
function drawDonut() {
|
|
444
|
-
ctxChart.clearRect(0, 0, 220, 220);
|
|
445
|
-
const cx = 110, cy = 110;
|
|
446
|
-
const baseRadius = 80;
|
|
447
|
-
const innerRadius = 55;
|
|
448
|
-
let currentAngle = -Math.PI / 2;
|
|
449
|
-
|
|
450
|
-
for (let i = 0; i < chartData.length; i++) {
|
|
451
|
-
const d = chartData[i];
|
|
452
|
-
if(d.value === 0) continue;
|
|
453
|
-
const sliceAngle = (d.value / totalVal) * (Math.PI * 2) * animationProgress;
|
|
454
|
-
const isHovered = (i === hoveredIndex);
|
|
455
|
-
const outerRadius = isHovered ? baseRadius + 10 : baseRadius;
|
|
456
|
-
|
|
457
|
-
ctxChart.beginPath();
|
|
458
|
-
ctxChart.arc(cx, cy, outerRadius, currentAngle, currentAngle + sliceAngle);
|
|
459
|
-
ctxChart.arc(cx, cy, innerRadius, currentAngle + sliceAngle, currentAngle, true);
|
|
460
|
-
ctxChart.closePath();
|
|
461
|
-
|
|
462
|
-
ctxChart.fillStyle = d.color;
|
|
463
|
-
// Hover glow effect
|
|
464
|
-
if (isHovered) {
|
|
465
|
-
ctxChart.shadowBlur = 15;
|
|
466
|
-
ctxChart.shadowColor = d.color;
|
|
467
|
-
} else {
|
|
468
|
-
ctxChart.shadowBlur = 0;
|
|
469
|
-
}
|
|
470
|
-
ctxChart.fill();
|
|
471
|
-
ctxChart.shadowBlur = 0; // reset
|
|
472
|
-
|
|
473
|
-
// Store angles for hit detection
|
|
474
|
-
d.startAngle = currentAngle;
|
|
475
|
-
d.endAngle = currentAngle + sliceAngle;
|
|
476
|
-
currentAngle += sliceAngle;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Draw tooltip
|
|
480
|
-
if (hoveredIndex > -1 && animationProgress > 0.9) {
|
|
481
|
-
const d = chartData[hoveredIndex];
|
|
482
|
-
const pct = ((d.value / totalVal) * 100).toFixed(1) + '%';
|
|
483
|
-
|
|
484
|
-
ctxChart.fillStyle = 'rgba(15, 23, 42, 0.9)';
|
|
485
|
-
ctxChart.strokeStyle = d.color;
|
|
486
|
-
ctxChart.lineWidth = 1;
|
|
487
|
-
ctxChart.beginPath();
|
|
488
|
-
ctxChart.roundRect(mousePos.x + 15, mousePos.y - 30, 90, 45, 6);
|
|
489
|
-
ctxChart.fill();
|
|
490
|
-
ctxChart.stroke();
|
|
491
|
-
|
|
492
|
-
ctxChart.fillStyle = '#fff';
|
|
493
|
-
ctxChart.font = 'bold 12px Inter';
|
|
494
|
-
ctxChart.fillText(pct, mousePos.x + 25, mousePos.y - 10);
|
|
495
|
-
ctxChart.fillStyle = '#cbd5e1';
|
|
496
|
-
ctxChart.font = '10px Inter';
|
|
497
|
-
ctxChart.fillText(d.value + ' items', mousePos.x + 25, mousePos.y + 5);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function animateDonut() {
|
|
502
|
-
if (animationProgress < 1) {
|
|
503
|
-
animationProgress += 0.04;
|
|
504
|
-
drawDonut();
|
|
505
|
-
requestAnimationFrame(animateDonut);
|
|
506
|
-
} else {
|
|
507
|
-
drawDonut();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
animateDonut();
|
|
511
|
-
|
|
512
|
-
document.getElementById('donutCanvas').addEventListener('mousemove', (e) => {
|
|
513
|
-
const rect = e.target.getBoundingClientRect();
|
|
514
|
-
const x = e.clientX - rect.left;
|
|
515
|
-
const y = e.clientY - rect.top;
|
|
516
|
-
mousePos = {x, y};
|
|
517
|
-
|
|
518
|
-
const cx = 110, cy = 110;
|
|
519
|
-
const dx = x - cx;
|
|
520
|
-
const dy = y - cy;
|
|
521
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
522
|
-
let angle = Math.atan2(dy, dx);
|
|
523
|
-
if (angle < -Math.PI / 2) angle += Math.PI * 2;
|
|
524
|
-
|
|
525
|
-
let found = -1;
|
|
526
|
-
if (dist >= 55 && dist <= 90) {
|
|
527
|
-
for (let i = 0; i < chartData.length; i++) {
|
|
528
|
-
if (chartData[i].value === 0) continue;
|
|
529
|
-
let start = chartData[i].startAngle;
|
|
530
|
-
let end = chartData[i].endAngle;
|
|
531
|
-
// Normalize angles relative to -PI/2
|
|
532
|
-
let nAngle = angle;
|
|
533
|
-
if (start > Math.PI && nAngle < 0) nAngle += Math.PI * 2;
|
|
534
|
-
if (nAngle >= start && nAngle <= end) {
|
|
535
|
-
found = i; break;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (found !== hoveredIndex) {
|
|
541
|
-
hoveredIndex = found;
|
|
542
|
-
document.getElementById('donutCanvas').style.cursor = found > -1 ? 'pointer' : 'default';
|
|
543
|
-
drawDonut();
|
|
544
|
-
} else if (found > -1) {
|
|
545
|
-
drawDonut(); // redraw tooltip position
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
document.getElementById('donutCanvas').addEventListener('mouseout', () => {
|
|
549
|
-
hoveredIndex = -1;
|
|
550
|
-
drawDonut();
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
// --- MOTOR CANVAS: FONDO DE NODOS AUM-IC ---
|
|
555
|
-
const canvas = document.getElementById('bgCanvas');
|
|
556
|
-
const ctx = canvas.getContext('2d');
|
|
557
|
-
let width, height;
|
|
558
|
-
let particles = [];
|
|
559
|
-
const mouse = { x: null, y: null };
|
|
560
|
-
|
|
561
|
-
function initBackground() {
|
|
562
|
-
width = canvas.width = window.innerWidth;
|
|
563
|
-
height = canvas.height = window.innerHeight;
|
|
564
|
-
particles = [];
|
|
565
|
-
const numParticles = Math.min(100, Math.floor((width * height) / 15000));
|
|
566
|
-
|
|
567
|
-
const colors = ['#22d3ee', '#a78bfa', '#34d399'];
|
|
568
|
-
for (let i = 0; i < numParticles; i++) {
|
|
569
|
-
particles.push({
|
|
570
|
-
x: Math.random() * width,
|
|
571
|
-
y: Math.random() * height,
|
|
572
|
-
vx: (Math.random() - 0.5) * 1.5,
|
|
573
|
-
vy: (Math.random() - 0.5) * 1.5,
|
|
574
|
-
radius: Math.random() * 2 + 1,
|
|
575
|
-
color: colors[Math.floor(Math.random() * colors.length)]
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
window.addEventListener('resize', initBackground);
|
|
580
|
-
window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; });
|
|
581
|
-
window.addEventListener('mouseout', () => { mouse.x = null; mouse.y = null; });
|
|
582
|
-
|
|
583
|
-
function animateBackground() {
|
|
584
|
-
ctx.clearRect(0, 0, width, height);
|
|
585
|
-
|
|
586
|
-
// Update and draw particles
|
|
587
|
-
for (let i = 0; i < particles.length; i++) {
|
|
588
|
-
const p = particles[i];
|
|
589
|
-
p.x += p.vx;
|
|
590
|
-
p.y += p.vy;
|
|
591
|
-
if (p.x < 0 || p.x > width) p.vx *= -1;
|
|
592
|
-
if (p.y < 0 || p.y > height) p.vy *= -1;
|
|
593
|
-
|
|
594
|
-
ctx.beginPath();
|
|
595
|
-
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
596
|
-
ctx.fillStyle = p.color;
|
|
597
|
-
ctx.fill();
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Connect particles
|
|
601
|
-
for (let i = 0; i < particles.length; i++) {
|
|
602
|
-
for (let j = i + 1; j < particles.length; j++) {
|
|
603
|
-
const p1 = particles[i], p2 = particles[j];
|
|
604
|
-
const dx = p1.x - p2.x, dy = p1.y - p2.y;
|
|
605
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
606
|
-
if (dist < 120) {
|
|
607
|
-
ctx.beginPath();
|
|
608
|
-
ctx.strokeStyle = \`rgba(0, 229, 255, \${0.15 - dist/120 * 0.15})\`;
|
|
609
|
-
ctx.lineWidth = 1.2;
|
|
610
|
-
ctx.moveTo(p1.x, p1.y);
|
|
611
|
-
ctx.lineTo(p2.x, p2.y);
|
|
612
|
-
ctx.stroke();
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// Connect to mouse
|
|
617
|
-
if (mouse.x != null) {
|
|
618
|
-
const dx = particles[i].x - mouse.x, dy = particles[i].y - mouse.y;
|
|
619
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
620
|
-
if (dist < 150) {
|
|
621
|
-
ctx.beginPath();
|
|
622
|
-
ctx.strokeStyle = \`rgba(167, 139, 250, \${0.2 - dist/150 * 0.2})\`;
|
|
623
|
-
ctx.lineWidth = 2.0;
|
|
624
|
-
ctx.moveTo(particles[i].x, particles[i].y);
|
|
625
|
-
ctx.lineTo(mouse.x, mouse.y);
|
|
626
|
-
ctx.stroke();
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
requestAnimationFrame(animateBackground);
|
|
631
|
-
}
|
|
632
|
-
initBackground();
|
|
633
|
-
animateBackground();
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
// --- LOGICA DE TABLA (PAGINADOR & ORDEN) ---
|
|
637
|
-
let sortAsc = false;
|
|
638
|
-
let currentPage = 1;
|
|
639
|
-
let pageSize = 20;
|
|
640
|
-
let filteredRows = [];
|
|
641
|
-
let allRows = [];
|
|
642
|
-
|
|
643
|
-
window.onload = () => {
|
|
644
|
-
allRows = Array.from(document.querySelectorAll('#tbody tr'));
|
|
645
|
-
filteredRows = [...allRows];
|
|
646
|
-
sortAsc = true;
|
|
647
|
-
sortTable(2, 'num');
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
function changePageSize(val) {
|
|
651
|
-
if (val === 'all') {
|
|
652
|
-
pageSize = filteredRows.length > 0 ? filteredRows.length : 10000;
|
|
653
|
-
} else {
|
|
654
|
-
pageSize = parseInt(val);
|
|
655
|
-
}
|
|
656
|
-
currentPage = 1;
|
|
657
|
-
renderTable();
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
function renderTable() {
|
|
661
|
-
const tbody = document.getElementById('tbody');
|
|
662
|
-
tbody.innerHTML = '';
|
|
663
|
-
const start = (currentPage - 1) * pageSize;
|
|
664
|
-
const end = start + pageSize;
|
|
665
|
-
const pageRows = filteredRows.slice(start, end);
|
|
666
|
-
pageRows.forEach(r => tbody.appendChild(r));
|
|
667
|
-
|
|
668
|
-
const totalPages = Math.ceil(filteredRows.length / pageSize) || 1;
|
|
669
|
-
document.getElementById('page-info').textContent = \`Mostrando \${start+1} - \${Math.min(end, filteredRows.length)} de \${filteredRows.length} registros\`;
|
|
670
|
-
|
|
671
|
-
renderPaginationControls(totalPages);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
function renderPaginationControls(totalPages) {
|
|
675
|
-
const cont = document.getElementById('pag-controls');
|
|
676
|
-
cont.innerHTML = '';
|
|
677
|
-
if (totalPages <= 1) return;
|
|
678
|
-
|
|
679
|
-
// Prev Button
|
|
680
|
-
const btnPrev = document.createElement('button');
|
|
681
|
-
btnPrev.className = 'pag-btn';
|
|
682
|
-
btnPrev.textContent = '«';
|
|
683
|
-
btnPrev.disabled = currentPage === 1;
|
|
684
|
-
btnPrev.onclick = () => { currentPage--; renderTable(); };
|
|
685
|
-
cont.appendChild(btnPrev);
|
|
686
|
-
|
|
687
|
-
// Page Numbers
|
|
688
|
-
let startPage = Math.max(1, currentPage - 2);
|
|
689
|
-
let endPage = Math.min(totalPages, currentPage + 2);
|
|
690
|
-
|
|
691
|
-
if (startPage > 1) {
|
|
692
|
-
cont.appendChild(createPageBtn(1));
|
|
693
|
-
if (startPage > 2) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
for (let i = startPage; i <= endPage; i++) {
|
|
697
|
-
cont.appendChild(createPageBtn(i));
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (endPage < totalPages) {
|
|
701
|
-
if (endPage < totalPages - 1) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
702
|
-
cont.appendChild(createPageBtn(totalPages));
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Next Button
|
|
706
|
-
const btnNext = document.createElement('button');
|
|
707
|
-
btnNext.className = 'pag-btn';
|
|
708
|
-
btnNext.textContent = '»';
|
|
709
|
-
btnNext.disabled = currentPage === totalPages;
|
|
710
|
-
btnNext.onclick = () => { currentPage++; renderTable(); };
|
|
711
|
-
cont.appendChild(btnNext);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
function createPageBtn(num) {
|
|
715
|
-
const btn = document.createElement('button');
|
|
716
|
-
btn.className = 'pag-btn' + (num === currentPage ? ' active' : '');
|
|
717
|
-
btn.textContent = num;
|
|
718
|
-
btn.onclick = () => { currentPage = num; renderTable(); };
|
|
719
|
-
return btn;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
function sortTable(colIdx, type) {
|
|
723
|
-
sortAsc = !sortAsc;
|
|
724
|
-
filteredRows.sort((a, b) => {
|
|
725
|
-
let valA, valB;
|
|
726
|
-
if (type === 'num') {
|
|
727
|
-
valA = parseInt(a.children[colIdx].getAttribute('data-occ') || '0');
|
|
728
|
-
valB = parseInt(b.children[colIdx].getAttribute('data-occ') || '0');
|
|
729
|
-
} else {
|
|
730
|
-
valA = a.children[colIdx].textContent.trim();
|
|
731
|
-
valB = b.children[colIdx].textContent.trim();
|
|
732
|
-
}
|
|
733
|
-
if (valA < valB) return sortAsc ? -1 : 1;
|
|
734
|
-
if (valA > valB) return sortAsc ? 1 : -1;
|
|
735
|
-
return 0;
|
|
736
|
-
});
|
|
737
|
-
currentPage = 1;
|
|
738
|
-
renderTable();
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
let activeCat = 'all';
|
|
742
|
-
let searchQuery = '';
|
|
743
|
-
|
|
744
|
-
function filter(cat, btn) {
|
|
745
|
-
document.querySelectorAll('.fbtn').forEach(b=>b.classList.remove('active'));
|
|
746
|
-
if(btn) btn.classList.add('active');
|
|
747
|
-
activeCat = cat;
|
|
748
|
-
applyFilters();
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function search(q) {
|
|
752
|
-
searchQuery = q;
|
|
753
|
-
applyFilters();
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
function applyFilters() {
|
|
757
|
-
let regex = null;
|
|
758
|
-
let lq = searchQuery.toLowerCase();
|
|
759
|
-
try { if (searchQuery) regex = new RegExp(searchQuery, 'i'); } catch(e) {}
|
|
760
|
-
|
|
761
|
-
filteredRows = allRows.filter(r => {
|
|
762
|
-
const catMatch = (activeCat === 'all' || r.getAttribute('data-cat') === activeCat);
|
|
763
|
-
if (!catMatch) return false;
|
|
764
|
-
|
|
765
|
-
const txt = r.textContent;
|
|
766
|
-
if (!searchQuery) return true;
|
|
767
|
-
if (regex && regex.test(txt)) return true;
|
|
768
|
-
return txt.toLowerCase().includes(lq);
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
currentPage = 1;
|
|
772
|
-
renderTable();
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
function copyText(el) {
|
|
776
|
-
const text = el.innerText;
|
|
777
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
778
|
-
const toast = document.getElementById('toast');
|
|
779
|
-
toast.classList.add('show');
|
|
780
|
-
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
function exportCSV() {
|
|
785
|
-
let csv = "Tailwind Original,AUM-IC Transmutada,Frecuencia,Nivel\\n";
|
|
786
|
-
filteredRows.forEach(r => {
|
|
787
|
-
const cols = r.querySelectorAll('td');
|
|
788
|
-
const orig = cols[0].innerText;
|
|
789
|
-
const aumic = cols[1].innerText;
|
|
790
|
-
const occ = cols[2].querySelector('span').innerText;
|
|
791
|
-
const lvl = cols[3].innerText;
|
|
792
|
-
csv += String.fromCharCode(34) + orig + String.fromCharCode(34) + "," + String.fromCharCode(34) + aumic + String.fromCharCode(34) + "," + occ + "," + String.fromCharCode(34) + lvl + String.fromCharCode(34) + "\\n";
|
|
793
|
-
});
|
|
794
|
-
const blob = new Blob([csv], { type: 'text/csv' });
|
|
795
|
-
const url = window.URL.createObjectURL(blob);
|
|
796
|
-
const a = document.createElement('a');
|
|
797
|
-
a.href = url;
|
|
798
|
-
a.download = 'aumic-report.csv';
|
|
799
|
-
a.click();
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
function exportMD() {
|
|
803
|
-
let md = "| Tailwind Original | AUM-IC Transmutada | Frecuencia | Nivel |\\n|---|---|---|---|\\n";
|
|
804
|
-
filteredRows.forEach(r => {
|
|
805
|
-
const cols = r.querySelectorAll('td');
|
|
806
|
-
const orig = cols[0].innerText.replace(/\|/g, '|');
|
|
807
|
-
const aumic = cols[1].innerText.replace(/\|/g, '|');
|
|
808
|
-
const occ = cols[2].querySelector('span').innerText;
|
|
809
|
-
const lvl = cols[3].innerText;
|
|
810
|
-
md += "| " + orig + " | " + aumic + " | " + occ + " | " + lvl + " |\\n";
|
|
811
|
-
});
|
|
812
|
-
const blob = new Blob([md], { type: 'text/markdown' });
|
|
813
|
-
const url = window.URL.createObjectURL(blob);
|
|
814
|
-
const a = document.createElement('a');
|
|
815
|
-
a.href = url;
|
|
816
|
-
a.download = 'aumic-report.md';
|
|
817
|
-
a.click();
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
document.addEventListener('keydown', (e) => {
|
|
821
|
-
if (e.key === '/' && document.activeElement.id !== 'searchInput') {
|
|
822
|
-
e.preventDefault();
|
|
823
|
-
document.getElementById('searchInput').focus();
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
|
|
168
|
+
const html = `<!DOCTYPE html>
|
|
169
|
+
<html lang="es"><head><meta charset="UTF-8"><title>AUM-IC · Dashboard C4I</title><style>
|
|
170
|
+
:root { --bg: #030712; --panel: rgba(15, 23, 42, 0.7); --panel-hover: #1e293b; --text: #e2e8f0; --muted: #64748b; --border: rgba(51, 65, 85, 0.6); --accent: #00e5ff; }
|
|
171
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
172
|
+
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;padding:2rem 3rem;position:relative;overflow-x:hidden;}
|
|
173
|
+
#bgCanvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
|
|
174
|
+
.content-wrapper { position: relative; z-index: 10; }
|
|
175
|
+
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid var(--border)}
|
|
176
|
+
header h1{font-size:1.8rem;font-weight:900;color:#fff;letter-spacing:-0.5px}header h1 span{color:var(--accent);text-shadow:0 0 10px rgba(0,229,255,0.4)}
|
|
177
|
+
.subtitle{font-size:0.85rem;color:var(--muted);margin-top:6px}.safe-tag{background:rgba(0,229,255,0.1);border:1px solid rgba(0,229,255,0.3);color:var(--accent);font-size:0.75rem;padding:6px 16px;border-radius:999px;font-weight:700;box-shadow:0 0 15px rgba(0,229,255,0.1)}
|
|
178
|
+
.dashboard-grid { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; }
|
|
179
|
+
.sidebar { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
180
|
+
.main-view { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
181
|
+
|
|
182
|
+
/* Panels */
|
|
183
|
+
.panel { background: var(--panel); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); padding: 1.5rem; border-radius: 16px; border: 1px solid var(--border); box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
|
|
184
|
+
.panel h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 1rem; }
|
|
185
|
+
|
|
186
|
+
/* Canvas Chart */
|
|
187
|
+
.chart-container { display: flex; justify-content: center; align-items: center; margin-bottom: 1rem; position: relative; height: 200px; }
|
|
188
|
+
.chart-center-overlay { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; pointer-events: none; }
|
|
189
|
+
.chart-center-overlay .score { font-size: 2.5rem; font-weight: 900; color: ${gradeColor}; line-height: 1; text-shadow: 0 0 15px ${gradeColor}44; }
|
|
190
|
+
.chart-center-overlay .score-lbl { font-size: 0.6rem; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-top: 4px; }
|
|
191
|
+
.legend { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 0.75rem; }
|
|
192
|
+
.leg-item { display: flex; align-items: center; gap: 6px; }
|
|
193
|
+
.leg-color { width: 10px; height: 10px; border-radius: 50%; }
|
|
194
|
+
|
|
195
|
+
/* Hero Metrics */
|
|
196
|
+
.metrics-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
|
|
197
|
+
.metric { background: rgba(0,0,0,0.3); padding: 1.2rem; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); backdrop-filter: blur(5px); }
|
|
198
|
+
.metric h3 { font-size: 1.8rem; font-weight: 900; color: #fff; line-height: 1; margin-bottom: 4px; }
|
|
199
|
+
.metric p { font-size: 0.7rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
200
|
+
.highlight { color: #34d399 !important; text-shadow: 0 0 10px rgba(52,211,153,0.4); }
|
|
201
|
+
.highlight-psi { color: #facc15 !important; }
|
|
202
|
+
|
|
203
|
+
/* Criminals */
|
|
204
|
+
.criminal { display: flex; gap: 1rem; align-items: center; padding: 10px; background: rgba(0,0,0,0.2); border-radius: 8px; margin-bottom: 8px; }
|
|
205
|
+
.c-rank { font-size: 1.2rem; font-weight: 900; color: #ef4444; width: 30px; text-align: center; }
|
|
206
|
+
.c-info { flex: 1; min-width: 0; }
|
|
207
|
+
.c-class { font-family: 'Fira Code', monospace; font-size: 0.75rem; color: #f472b6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; }
|
|
208
|
+
.c-stats { font-size: 0.65rem; color: var(--muted); }
|
|
209
|
+
|
|
210
|
+
/* Table Controls */
|
|
211
|
+
.toolbar{display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap; margin-bottom: 1.5rem;}
|
|
212
|
+
.fbtn{background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#94a3b8;font-size:0.75rem;padding:8px 16px;border-radius:8px;cursor:pointer;transition:all 0.2s;font-weight:500}
|
|
213
|
+
.fbtn:hover,.fbtn.active{color:#fff;border-color:var(--accent);background:rgba(0,229,255,0.1);box-shadow:0 0 10px rgba(0,229,255,0.2)}
|
|
214
|
+
.search{margin-left:auto;background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.8rem;padding:8px 16px;border-radius:8px;outline:none;width:220px;transition:all 0.2s}
|
|
215
|
+
.search:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,229,255,0.2)}
|
|
216
|
+
.page-size-sel { background: rgba(15, 23, 42, 0.9); color: #fff; background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.75rem;padding:8px;border-radius:8px;outline:none; cursor:pointer;}
|
|
217
|
+
|
|
218
|
+
/* Table */
|
|
219
|
+
.table-container{border-radius:12px;border:1px solid var(--border);overflow:hidden;background:rgba(0,0,0,0.2);}
|
|
220
|
+
table{width:100%;border-collapse:collapse;font-size:0.8rem}
|
|
221
|
+
th{background:rgba(0,0,0,0.3);color:var(--muted);text-align:left;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;text-transform:uppercase;letter-spacing:1px;font-size:0.7rem;cursor:pointer;user-select:none}
|
|
222
|
+
th:hover{color:#fff}
|
|
223
|
+
td{padding:12px 16px;border-bottom:1px solid rgba(255,255,255,0.02);vertical-align:middle;transition:background 0.15s}
|
|
224
|
+
tr:hover td{background:rgba(255,255,255,0.05)}
|
|
225
|
+
.tw-cell{font-family:'Fira Code',Consolas,monospace;color:#f472b6;font-size:0.75rem;max-width:300px;word-break:break-all}
|
|
226
|
+
.aumic-cell{font-family:'Fira Code',Consolas,monospace;color:#38bdf8;font-size:0.75rem;white-space:nowrap;font-weight:600}
|
|
227
|
+
.occ-cell{display:flex;align-items:center;gap:10px;font-family:'Fira Code',monospace;font-weight:bold;color:#fff}
|
|
228
|
+
.occ-bar{height:6px;border-radius:3px;opacity:0.8}
|
|
229
|
+
.badge{font-size:0.65rem;padding:4px 10px;border-radius:999px;border:1px solid;font-weight:800;white-space:nowrap;text-transform:uppercase;letter-spacing:0.5px}
|
|
230
|
+
|
|
231
|
+
/* Pagination */
|
|
232
|
+
.pagination { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: rgba(0,0,0,0.3); border-top: 1px solid var(--border); }
|
|
233
|
+
.pag-controls { display: flex; gap: 6px; align-items: center; background: rgba(15,23,42,0.6); padding: 4px; border-radius: 8px; border: 1px solid var(--border); }
|
|
234
|
+
.pag-btn { background: transparent; border: none; color: var(--muted); padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; transition: 0.2s; min-width: 32px; text-align: center; font-weight: 600; }
|
|
235
|
+
.pag-btn:hover:not(:disabled) { background: rgba(255,255,255,0.05); color: #fff; }
|
|
236
|
+
.pag-btn.active { background: rgba(0,229,255,0.15); color: var(--accent); box-shadow: inset 0 0 0 1px rgba(0,229,255,0.3); }
|
|
237
|
+
.pag-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
238
|
+
.pag-info { font-size: 0.75rem; color: var(--muted); }
|
|
239
|
+
.pag-dots { color: var(--muted); font-size: 0.75rem; font-weight: bold; padding: 0 4px; }
|
|
240
|
+
|
|
241
|
+
/* Footer */
|
|
242
|
+
.footer { position: relative; bottom: 0; left: 0; width: 100%; text-align: center; padding: 20px; font-size: 0.75rem; color: var(--muted); border-top: 1px solid rgba(255,255,255,0.05); background: rgba(0,0,0,0.3); backdrop-filter: blur(5px); margin-top: auto; }
|
|
243
|
+
.footer a { color: var(--accent); text-decoration: none; font-weight: 600; transition: color 0.2s; }
|
|
244
|
+
.footer a:hover { color: #fff; text-shadow: 0 0 10px var(--accent); }
|
|
245
|
+
.page-size-sel option { background: #0f172a; color: #fff; }
|
|
246
|
+
/* Export & Toast */
|
|
247
|
+
.export-group-top { display: flex; gap: 8px; margin-left: auto; align-items: center; } .export-group-top .fbtn { background: rgba(0,229,255,0.05); }
|
|
248
|
+
.toast { position: fixed; bottom: 20px; right: 20px; background: rgba(0, 229, 255, 0.15); border: 1px solid var(--accent); color: #fff; padding: 10px 20px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; box-shadow: 0 5px 15px rgba(0,229,255,0.2); backdrop-filter: blur(5px); transform: translateY(100px); opacity: 0; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 9999; }
|
|
249
|
+
.toast.show { transform: translateY(0); opacity: 1; }
|
|
250
|
+
.copyable { cursor: pointer; transition: color 0.2s; position: relative; }
|
|
251
|
+
.copyable:hover { color: #fff; text-shadow: 0 0 8px rgba(255,255,255,0.5); }
|
|
252
|
+
.copyable::after { content: 'Copiar'; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); color: #fff; font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; opacity: 0; pointer-events: none; transition: opacity 0.2s; }
|
|
253
|
+
.copyable:hover::after { opacity: 1; }
|
|
254
|
+
|
|
255
|
+
/* Sticky Header */
|
|
256
|
+
.table-container { overflow-x: auto; }
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
/* Print PDF (Mesa Directiva) */
|
|
260
|
+
@media print {
|
|
261
|
+
body { background: #fff !important; color: #000 !important; padding: 0 !important; font-size: 10pt; }
|
|
262
|
+
#bgCanvas, .toolbar, .pagination, .safe-tag, .export-group-top, .footer, .toast { display: none !important; }
|
|
263
|
+
.content-wrapper { padding: 0 !important; max-width: 100% !important; }
|
|
264
|
+
header { border-bottom: 2px solid #000; padding-bottom: 10px; margin-bottom: 20px; flex-direction: column !important; align-items: flex-start !important; }
|
|
265
|
+
header h1 { color: #000 !important; text-shadow: none !important; margin: 0; }
|
|
266
|
+
header h1 span { color: #000 !important; }
|
|
267
|
+
|
|
268
|
+
.dashboard-grid { display: block !important; }
|
|
269
|
+
.sidebar { display: flex !important; flex-direction: row !important; gap: 20px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
270
|
+
.sidebar .panel { flex: 1; border: 1px solid #cbd5e1 !important; padding: 15px !important; border-radius: 8px !important; }
|
|
271
|
+
|
|
272
|
+
.panel { background: none !important; border: none !important; box-shadow: none !important; padding: 0 !important; }
|
|
273
|
+
|
|
274
|
+
.metrics-grid { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 10px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
275
|
+
.metric { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; padding: 10px !important; border-radius: 8px !important; }
|
|
276
|
+
.metric h3 { color: #000 !important; text-shadow: none !important; font-size: 1.2rem; margin-bottom: 5px; }
|
|
277
|
+
.metric p { color: #333 !important; font-size: 0.8rem; }
|
|
278
|
+
|
|
279
|
+
.criteria-box { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; color: #000 !important; }
|
|
280
|
+
.criteria-box strong { color: #000 !important; }
|
|
281
|
+
.criteria-box span { color: #000 !important; font-weight: bold; }
|
|
282
|
+
|
|
283
|
+
.chart-center-overlay .score { color: #000 !important; text-shadow: none !important; }
|
|
284
|
+
.chart-center-overlay .score-lbl { color: #333 !important; }
|
|
285
|
+
|
|
286
|
+
.leg-item { color: #000 !important; }
|
|
287
|
+
|
|
288
|
+
.table-container { max-height: none !important; border: none !important; overflow: visible !important; }
|
|
289
|
+
table { border-collapse: collapse !important; width: 100% !important; page-break-inside: auto; }
|
|
290
|
+
tr { page-break-inside: avoid; page-break-after: auto; }
|
|
291
|
+
th, td { border: 1px solid #cbd5e1 !important; color: #000 !important; padding: 6px !important; font-size: 9pt !important; }
|
|
292
|
+
th { background: #f1f5f9 !important; color: #000 !important; font-weight: bold; }
|
|
293
|
+
.tw-cell, .aumic-cell { color: #000 !important; font-weight: normal; }
|
|
294
|
+
.occ-bar { background: #94a3b8 !important; }
|
|
295
|
+
|
|
296
|
+
@page { margin: 1cm; size: auto; }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
</style></head><body style="display: flex; flex-direction: column;">
|
|
300
|
+
|
|
301
|
+
<canvas id="bgCanvas"></canvas>
|
|
302
|
+
|
|
303
|
+
<div class="content-wrapper">
|
|
304
|
+
<header>
|
|
305
|
+
<div><h1>⚔️ <span>AUM-IC</span> · Centro de Inteligencia</h1>
|
|
306
|
+
<p class="subtitle">Análisis Forense completado exitosamente sin mutación en disco.</p></div>
|
|
307
|
+
<div class="export-group-top" style="display:flex; gap:8px; margin-left: 20px;">
|
|
308
|
+
<button class="fbtn" onclick="exportCSV()">⬇️ CSV</button><button class="fbtn" onclick="exportMD()">⬇️ MD</button><button class="fbtn" onclick="window.print()">🖨️ PDF</button>
|
|
309
|
+
</div>
|
|
310
|
+
<div class="safe-tag" style="align-items: flex-end; display: flex; flex-direction: column;">
|
|
311
|
+
<span>${isSimulate ? 'MODO DRY-RUN ACTIVO' : 'MUTACIÓN AUM-IC COMPLETADA'}</span>
|
|
312
|
+
<span style="font-size: 0.65rem; opacity: 0.8; margin-top: 2px;">v${pkgVersion}</span>
|
|
313
|
+
</div>
|
|
314
|
+
</header>
|
|
315
|
+
|
|
316
|
+
<main class="dashboard-grid">
|
|
317
|
+
<aside class="sidebar">
|
|
318
|
+
<article class="panel">
|
|
319
|
+
<h2>Mapeo Estructural</h2>
|
|
320
|
+
<div class="chart-container">
|
|
321
|
+
<canvas id="donutCanvas" width="220" height="220"></canvas>
|
|
322
|
+
<div class="chart-center-overlay">
|
|
323
|
+
<div class="score">${grade}</div>
|
|
324
|
+
<div class="score-lbl">Rank</div>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
<div class="legend">
|
|
328
|
+
<div class="leg-item"><div class="leg-color" style="background:#22d3ee"></div>Átomos (${counts.atoms})</div>
|
|
329
|
+
<div class="leg-item"><div class="leg-color" style="background:#a78bfa"></div>Moléculas (${counts.molecules})</div>
|
|
330
|
+
<div class="leg-item"><div class="leg-color" style="background:#34d399"></div>Organismos (${counts.organisms})</div>
|
|
331
|
+
<div class="leg-item"><div class="leg-color" style="background:#fb923c"></div>Ecosistemas (${counts.ecosystems})</div>
|
|
332
|
+
<div class="leg-item"><div class="leg-color" style="background:#facc15"></div>Galaxias (${counts.galaxies})</div>
|
|
333
|
+
</div>
|
|
334
|
+
<div class="criteria-box" style="margin-top: 1.5rem; padding: 1rem; background: rgba(0,0,0,0.2); border-radius: 8px; border: 1px solid var(--border); font-size: 0.75rem; color: var(--muted); line-height: 1.5;">
|
|
335
|
+
<strong style="color: #fff; margin-bottom: 0.5rem; display: block;">Criterio de Evaluación AUM-IC</strong>
|
|
336
|
+
<ul style="margin: 0; padding-left: 1rem; list-style: square;">
|
|
337
|
+
<li><span style="color:#22d3ee">Átomos:</span> Utilidades base simples (p-4, m-2, flex).</li>
|
|
338
|
+
<li><span style="color:#a78bfa">Moléculas:</span> Estilos condicionales y variantes (hover:, focus:).</li>
|
|
339
|
+
<li><span style="color:#34d399">Organismos:</span> Cadenas largas y componentes estructurales.</li>
|
|
340
|
+
<li><span style="color:#fb923c">Ecosistemas:</span> Combinaciones de Layouts complejos (Grid/Flex).</li>
|
|
341
|
+
<li><span style="color:#facc15">Galaxias:</span> Declaraciones globales masivas o de alta densidad.</li>
|
|
342
|
+
</ul>
|
|
343
|
+
<div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid rgba(255,255,255,0.1);">
|
|
344
|
+
<strong style="color: #fff; display:block; margin-bottom: 0.3rem;">🏆 Rank (Score) & Eficiencia</strong>
|
|
345
|
+
<div style="margin-bottom: 0.6rem; color: #94a3b8;">La <b>Eficiencia de Refactorización</b> mide el porcentaje exacto de peso (bytes) que AUM-IC logró purgar del DOM frente al HTML inflado original.</div>
|
|
346
|
+
<div style="display: flex; gap: 6px; flex-wrap: wrap; font-size: 0.7rem;">
|
|
347
|
+
<span style="background: rgba(250,204,21,0.15); color:#facc15; padding: 3px 6px; border-radius: 4px; border: 1px solid #facc1544;"><b>S</b> ≥ 70%</span>
|
|
348
|
+
<span style="background: rgba(52,211,153,0.15); color:#34d399; padding: 3px 6px; border-radius: 4px; border: 1px solid #34d39944;"><b>A</b> ≥ 50%</span>
|
|
349
|
+
<span style="background: rgba(34,211,238,0.15); color:#22d3ee; padding: 3px 6px; border-radius: 4px; border: 1px solid #22d3ee44;"><b>B</b> ≥ 30%</span>
|
|
350
|
+
<span style="background: rgba(167,139,250,0.15); color:#a78bfa; padding: 3px 6px; border-radius: 4px; border: 1px solid #a78bfa44;"><b>C</b> ≥ 15%</span>
|
|
351
|
+
<span style="background: rgba(239,68,68,0.15); color:#ef4444; padding: 3px 6px; border-radius: 4px; border: 1px solid #ef444444;"><b>D</b> < 15%</span>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
</article>
|
|
356
|
+
|
|
357
|
+
<article class="panel">
|
|
358
|
+
<h2>☠️ Top 5 Ofensores del DOM</h2>
|
|
359
|
+
${top5}
|
|
360
|
+
</article>
|
|
361
|
+
</aside>
|
|
362
|
+
|
|
363
|
+
<section class="main-view">
|
|
364
|
+
<div class="metrics-grid">
|
|
365
|
+
<div class="metric">
|
|
366
|
+
<h3 class="highlight">🔥 ${savedKB} KB</h3>
|
|
367
|
+
<p>Peso/Tamaño Estimado Salvado</p>
|
|
368
|
+
</div>
|
|
369
|
+
<div class="metric">
|
|
370
|
+
<h3 class="highlight-psi">🚀 +${psiImpact} pts</h3>
|
|
371
|
+
<p>Impacto Estimado Google PSI</p>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="metric">
|
|
374
|
+
<h3>${efficiency}%</h3>
|
|
375
|
+
<p>Eficiencia de Refactorización</p>
|
|
376
|
+
</div>
|
|
377
|
+
<div class="metric">
|
|
378
|
+
<h3>${mapping.size}</h3>
|
|
379
|
+
<p>Clases Únicas Tailwind</p>
|
|
380
|
+
</div>
|
|
381
|
+
<div class="metric">
|
|
382
|
+
<h3>${totalOccurrences}</h3>
|
|
383
|
+
<p>Instancias Totales Detectadas</p>
|
|
384
|
+
</div>
|
|
385
|
+
<div class="metric">
|
|
386
|
+
<h3>${totalOccurrences - mapping.size}</h3>
|
|
387
|
+
<p>Redundancia Estructural (Repetidas)</p>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<article class="panel">
|
|
392
|
+
<nav class="toolbar">
|
|
393
|
+
<button class="fbtn active" onclick="filter('all',this)">Todos</button>
|
|
394
|
+
<button class="fbtn" onclick="filter('atoms',this)">Átomos</button>
|
|
395
|
+
<button class="fbtn" onclick="filter('molecules',this)">Moléculas</button>
|
|
396
|
+
<button class="fbtn" onclick="filter('organisms',this)">Organismos</button>
|
|
397
|
+
<button class="fbtn" onclick="filter('ecosystems',this)">Ecosistemas</button>
|
|
398
|
+
<button class="fbtn" onclick="filter('galaxies',this)">Galaxias</button>
|
|
399
|
+
|
|
400
|
+
<input class="search" type="text" id="searchInput" placeholder="Buscar clase (Regex)..." oninput="search(this.value)">
|
|
401
|
+
<select class="page-size-sel" id="pageSizeSelect" onchange="changePageSize(this.value)">
|
|
402
|
+
<option value="20">20 por pág</option>
|
|
403
|
+
<option value="50">50 por pág</option>
|
|
404
|
+
<option value="100">100 por pág</option>
|
|
405
|
+
<option value="all">TODOS</option>
|
|
406
|
+
</select>
|
|
407
|
+
</nav>
|
|
408
|
+
<section class="table-container">
|
|
409
|
+
<table id="table">
|
|
410
|
+
<thead><tr>
|
|
411
|
+
<th onclick="sortTable(0, 'str')">Clase Original (Tailwind) ↕</th>
|
|
412
|
+
<th onclick="sortTable(1, 'str')">Clase AUM-IC Transmutada ↕</th>
|
|
413
|
+
<th onclick="sortTable(2, 'num')">Frecuencia ↕</th>
|
|
414
|
+
<th onclick="sortTable(3, 'str')">Nivel ↕</th>
|
|
415
|
+
</tr></thead>
|
|
416
|
+
<tbody id="tbody">${rows.join('')}</tbody>
|
|
417
|
+
</table>
|
|
418
|
+
<div class="pagination">
|
|
419
|
+
<span class="pag-info" id="page-info"></span>
|
|
420
|
+
<div class="pag-controls" id="pag-controls"></div>
|
|
421
|
+
</div>
|
|
422
|
+
</section>
|
|
423
|
+
</article>
|
|
424
|
+
</section>
|
|
425
|
+
</main>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
<div id="toast" class="toast">✔ Copiado al portapapeles</div>
|
|
429
|
+
<footer class="footer">
|
|
430
|
+
Forged by <a href="https://ingcrea.com" target="_blank">INGENIERÍA CREATIVA Y DESARROLLOS TECNOLÓGICOS S.A.S.</a> |
|
|
431
|
+
<a href="https://github.com/ingcrea/aumic-tailwind-killer" target="_blank">View on GitHub</a>
|
|
432
|
+
</footer>
|
|
433
|
+
|
|
434
|
+
<script>
|
|
435
|
+
// --- MOTOR CANVAS: DONUT CHART ANIMADO ---
|
|
436
|
+
const chartData = ${JSON.stringify(chartData)};
|
|
437
|
+
const ctxChart = document.getElementById('donutCanvas').getContext('2d');
|
|
438
|
+
let totalVal = chartData.reduce((acc, d) => acc + d.value, 0);
|
|
439
|
+
if(totalVal === 0) totalVal = 1;
|
|
440
|
+
let animationProgress = 0;
|
|
441
|
+
let hoveredIndex = -1;
|
|
442
|
+
let mousePos = {x: 0, y: 0};
|
|
443
|
+
|
|
444
|
+
function drawDonut() {
|
|
445
|
+
ctxChart.clearRect(0, 0, 220, 220);
|
|
446
|
+
const cx = 110, cy = 110;
|
|
447
|
+
const baseRadius = 80;
|
|
448
|
+
const innerRadius = 55;
|
|
449
|
+
let currentAngle = -Math.PI / 2;
|
|
450
|
+
|
|
451
|
+
for (let i = 0; i < chartData.length; i++) {
|
|
452
|
+
const d = chartData[i];
|
|
453
|
+
if(d.value === 0) continue;
|
|
454
|
+
const sliceAngle = (d.value / totalVal) * (Math.PI * 2) * animationProgress;
|
|
455
|
+
const isHovered = (i === hoveredIndex);
|
|
456
|
+
const outerRadius = isHovered ? baseRadius + 10 : baseRadius;
|
|
457
|
+
|
|
458
|
+
ctxChart.beginPath();
|
|
459
|
+
ctxChart.arc(cx, cy, outerRadius, currentAngle, currentAngle + sliceAngle);
|
|
460
|
+
ctxChart.arc(cx, cy, innerRadius, currentAngle + sliceAngle, currentAngle, true);
|
|
461
|
+
ctxChart.closePath();
|
|
462
|
+
|
|
463
|
+
ctxChart.fillStyle = d.color;
|
|
464
|
+
// Hover glow effect
|
|
465
|
+
if (isHovered) {
|
|
466
|
+
ctxChart.shadowBlur = 15;
|
|
467
|
+
ctxChart.shadowColor = d.color;
|
|
468
|
+
} else {
|
|
469
|
+
ctxChart.shadowBlur = 0;
|
|
470
|
+
}
|
|
471
|
+
ctxChart.fill();
|
|
472
|
+
ctxChart.shadowBlur = 0; // reset
|
|
473
|
+
|
|
474
|
+
// Store angles for hit detection
|
|
475
|
+
d.startAngle = currentAngle;
|
|
476
|
+
d.endAngle = currentAngle + sliceAngle;
|
|
477
|
+
currentAngle += sliceAngle;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Draw tooltip
|
|
481
|
+
if (hoveredIndex > -1 && animationProgress > 0.9) {
|
|
482
|
+
const d = chartData[hoveredIndex];
|
|
483
|
+
const pct = ((d.value / totalVal) * 100).toFixed(1) + '%';
|
|
484
|
+
|
|
485
|
+
ctxChart.fillStyle = 'rgba(15, 23, 42, 0.9)';
|
|
486
|
+
ctxChart.strokeStyle = d.color;
|
|
487
|
+
ctxChart.lineWidth = 1;
|
|
488
|
+
ctxChart.beginPath();
|
|
489
|
+
ctxChart.roundRect(mousePos.x + 15, mousePos.y - 30, 90, 45, 6);
|
|
490
|
+
ctxChart.fill();
|
|
491
|
+
ctxChart.stroke();
|
|
492
|
+
|
|
493
|
+
ctxChart.fillStyle = '#fff';
|
|
494
|
+
ctxChart.font = 'bold 12px Inter';
|
|
495
|
+
ctxChart.fillText(pct, mousePos.x + 25, mousePos.y - 10);
|
|
496
|
+
ctxChart.fillStyle = '#cbd5e1';
|
|
497
|
+
ctxChart.font = '10px Inter';
|
|
498
|
+
ctxChart.fillText(d.value + ' items', mousePos.x + 25, mousePos.y + 5);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function animateDonut() {
|
|
503
|
+
if (animationProgress < 1) {
|
|
504
|
+
animationProgress += 0.04;
|
|
505
|
+
drawDonut();
|
|
506
|
+
requestAnimationFrame(animateDonut);
|
|
507
|
+
} else {
|
|
508
|
+
drawDonut();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
animateDonut();
|
|
512
|
+
|
|
513
|
+
document.getElementById('donutCanvas').addEventListener('mousemove', (e) => {
|
|
514
|
+
const rect = e.target.getBoundingClientRect();
|
|
515
|
+
const x = e.clientX - rect.left;
|
|
516
|
+
const y = e.clientY - rect.top;
|
|
517
|
+
mousePos = {x, y};
|
|
518
|
+
|
|
519
|
+
const cx = 110, cy = 110;
|
|
520
|
+
const dx = x - cx;
|
|
521
|
+
const dy = y - cy;
|
|
522
|
+
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
523
|
+
let angle = Math.atan2(dy, dx);
|
|
524
|
+
if (angle < -Math.PI / 2) angle += Math.PI * 2;
|
|
525
|
+
|
|
526
|
+
let found = -1;
|
|
527
|
+
if (dist >= 55 && dist <= 90) {
|
|
528
|
+
for (let i = 0; i < chartData.length; i++) {
|
|
529
|
+
if (chartData[i].value === 0) continue;
|
|
530
|
+
let start = chartData[i].startAngle;
|
|
531
|
+
let end = chartData[i].endAngle;
|
|
532
|
+
// Normalize angles relative to -PI/2
|
|
533
|
+
let nAngle = angle;
|
|
534
|
+
if (start > Math.PI && nAngle < 0) nAngle += Math.PI * 2;
|
|
535
|
+
if (nAngle >= start && nAngle <= end) {
|
|
536
|
+
found = i; break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (found !== hoveredIndex) {
|
|
542
|
+
hoveredIndex = found;
|
|
543
|
+
document.getElementById('donutCanvas').style.cursor = found > -1 ? 'pointer' : 'default';
|
|
544
|
+
drawDonut();
|
|
545
|
+
} else if (found > -1) {
|
|
546
|
+
drawDonut(); // redraw tooltip position
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
document.getElementById('donutCanvas').addEventListener('mouseout', () => {
|
|
550
|
+
hoveredIndex = -1;
|
|
551
|
+
drawDonut();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
// --- MOTOR CANVAS: FONDO DE NODOS AUM-IC ---
|
|
556
|
+
const canvas = document.getElementById('bgCanvas');
|
|
557
|
+
const ctx = canvas.getContext('2d');
|
|
558
|
+
let width, height;
|
|
559
|
+
let particles = [];
|
|
560
|
+
const mouse = { x: null, y: null };
|
|
561
|
+
|
|
562
|
+
function initBackground() {
|
|
563
|
+
width = canvas.width = window.innerWidth;
|
|
564
|
+
height = canvas.height = window.innerHeight;
|
|
565
|
+
particles = [];
|
|
566
|
+
const numParticles = Math.min(100, Math.floor((width * height) / 15000));
|
|
567
|
+
|
|
568
|
+
const colors = ['#22d3ee', '#a78bfa', '#34d399'];
|
|
569
|
+
for (let i = 0; i < numParticles; i++) {
|
|
570
|
+
particles.push({
|
|
571
|
+
x: Math.random() * width,
|
|
572
|
+
y: Math.random() * height,
|
|
573
|
+
vx: (Math.random() - 0.5) * 1.5,
|
|
574
|
+
vy: (Math.random() - 0.5) * 1.5,
|
|
575
|
+
radius: Math.random() * 2 + 1,
|
|
576
|
+
color: colors[Math.floor(Math.random() * colors.length)]
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
window.addEventListener('resize', initBackground);
|
|
581
|
+
window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; });
|
|
582
|
+
window.addEventListener('mouseout', () => { mouse.x = null; mouse.y = null; });
|
|
583
|
+
|
|
584
|
+
function animateBackground() {
|
|
585
|
+
ctx.clearRect(0, 0, width, height);
|
|
586
|
+
|
|
587
|
+
// Update and draw particles
|
|
588
|
+
for (let i = 0; i < particles.length; i++) {
|
|
589
|
+
const p = particles[i];
|
|
590
|
+
p.x += p.vx;
|
|
591
|
+
p.y += p.vy;
|
|
592
|
+
if (p.x < 0 || p.x > width) p.vx *= -1;
|
|
593
|
+
if (p.y < 0 || p.y > height) p.vy *= -1;
|
|
594
|
+
|
|
595
|
+
ctx.beginPath();
|
|
596
|
+
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
597
|
+
ctx.fillStyle = p.color;
|
|
598
|
+
ctx.fill();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Connect particles
|
|
602
|
+
for (let i = 0; i < particles.length; i++) {
|
|
603
|
+
for (let j = i + 1; j < particles.length; j++) {
|
|
604
|
+
const p1 = particles[i], p2 = particles[j];
|
|
605
|
+
const dx = p1.x - p2.x, dy = p1.y - p2.y;
|
|
606
|
+
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
607
|
+
if (dist < 120) {
|
|
608
|
+
ctx.beginPath();
|
|
609
|
+
ctx.strokeStyle = \`rgba(0, 229, 255, \${0.15 - dist/120 * 0.15})\`;
|
|
610
|
+
ctx.lineWidth = 1.2;
|
|
611
|
+
ctx.moveTo(p1.x, p1.y);
|
|
612
|
+
ctx.lineTo(p2.x, p2.y);
|
|
613
|
+
ctx.stroke();
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Connect to mouse
|
|
618
|
+
if (mouse.x != null) {
|
|
619
|
+
const dx = particles[i].x - mouse.x, dy = particles[i].y - mouse.y;
|
|
620
|
+
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
621
|
+
if (dist < 150) {
|
|
622
|
+
ctx.beginPath();
|
|
623
|
+
ctx.strokeStyle = \`rgba(167, 139, 250, \${0.2 - dist/150 * 0.2})\`;
|
|
624
|
+
ctx.lineWidth = 2.0;
|
|
625
|
+
ctx.moveTo(particles[i].x, particles[i].y);
|
|
626
|
+
ctx.lineTo(mouse.x, mouse.y);
|
|
627
|
+
ctx.stroke();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
requestAnimationFrame(animateBackground);
|
|
632
|
+
}
|
|
633
|
+
initBackground();
|
|
634
|
+
animateBackground();
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
// --- LOGICA DE TABLA (PAGINADOR & ORDEN) ---
|
|
638
|
+
let sortAsc = false;
|
|
639
|
+
let currentPage = 1;
|
|
640
|
+
let pageSize = 20;
|
|
641
|
+
let filteredRows = [];
|
|
642
|
+
let allRows = [];
|
|
643
|
+
|
|
644
|
+
window.onload = () => {
|
|
645
|
+
allRows = Array.from(document.querySelectorAll('#tbody tr'));
|
|
646
|
+
filteredRows = [...allRows];
|
|
647
|
+
sortAsc = true;
|
|
648
|
+
sortTable(2, 'num');
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
function changePageSize(val) {
|
|
652
|
+
if (val === 'all') {
|
|
653
|
+
pageSize = filteredRows.length > 0 ? filteredRows.length : 10000;
|
|
654
|
+
} else {
|
|
655
|
+
pageSize = parseInt(val);
|
|
656
|
+
}
|
|
657
|
+
currentPage = 1;
|
|
658
|
+
renderTable();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function renderTable() {
|
|
662
|
+
const tbody = document.getElementById('tbody');
|
|
663
|
+
tbody.innerHTML = '';
|
|
664
|
+
const start = (currentPage - 1) * pageSize;
|
|
665
|
+
const end = start + pageSize;
|
|
666
|
+
const pageRows = filteredRows.slice(start, end);
|
|
667
|
+
pageRows.forEach(r => tbody.appendChild(r));
|
|
668
|
+
|
|
669
|
+
const totalPages = Math.ceil(filteredRows.length / pageSize) || 1;
|
|
670
|
+
document.getElementById('page-info').textContent = \`Mostrando \${start+1} - \${Math.min(end, filteredRows.length)} de \${filteredRows.length} registros\`;
|
|
671
|
+
|
|
672
|
+
renderPaginationControls(totalPages);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function renderPaginationControls(totalPages) {
|
|
676
|
+
const cont = document.getElementById('pag-controls');
|
|
677
|
+
cont.innerHTML = '';
|
|
678
|
+
if (totalPages <= 1) return;
|
|
679
|
+
|
|
680
|
+
// Prev Button
|
|
681
|
+
const btnPrev = document.createElement('button');
|
|
682
|
+
btnPrev.className = 'pag-btn';
|
|
683
|
+
btnPrev.textContent = '«';
|
|
684
|
+
btnPrev.disabled = currentPage === 1;
|
|
685
|
+
btnPrev.onclick = () => { currentPage--; renderTable(); };
|
|
686
|
+
cont.appendChild(btnPrev);
|
|
687
|
+
|
|
688
|
+
// Page Numbers
|
|
689
|
+
let startPage = Math.max(1, currentPage - 2);
|
|
690
|
+
let endPage = Math.min(totalPages, currentPage + 2);
|
|
691
|
+
|
|
692
|
+
if (startPage > 1) {
|
|
693
|
+
cont.appendChild(createPageBtn(1));
|
|
694
|
+
if (startPage > 2) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
for (let i = startPage; i <= endPage; i++) {
|
|
698
|
+
cont.appendChild(createPageBtn(i));
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (endPage < totalPages) {
|
|
702
|
+
if (endPage < totalPages - 1) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
703
|
+
cont.appendChild(createPageBtn(totalPages));
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Next Button
|
|
707
|
+
const btnNext = document.createElement('button');
|
|
708
|
+
btnNext.className = 'pag-btn';
|
|
709
|
+
btnNext.textContent = '»';
|
|
710
|
+
btnNext.disabled = currentPage === totalPages;
|
|
711
|
+
btnNext.onclick = () => { currentPage++; renderTable(); };
|
|
712
|
+
cont.appendChild(btnNext);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function createPageBtn(num) {
|
|
716
|
+
const btn = document.createElement('button');
|
|
717
|
+
btn.className = 'pag-btn' + (num === currentPage ? ' active' : '');
|
|
718
|
+
btn.textContent = num;
|
|
719
|
+
btn.onclick = () => { currentPage = num; renderTable(); };
|
|
720
|
+
return btn;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function sortTable(colIdx, type) {
|
|
724
|
+
sortAsc = !sortAsc;
|
|
725
|
+
filteredRows.sort((a, b) => {
|
|
726
|
+
let valA, valB;
|
|
727
|
+
if (type === 'num') {
|
|
728
|
+
valA = parseInt(a.children[colIdx].getAttribute('data-occ') || '0');
|
|
729
|
+
valB = parseInt(b.children[colIdx].getAttribute('data-occ') || '0');
|
|
730
|
+
} else {
|
|
731
|
+
valA = a.children[colIdx].textContent.trim();
|
|
732
|
+
valB = b.children[colIdx].textContent.trim();
|
|
733
|
+
}
|
|
734
|
+
if (valA < valB) return sortAsc ? -1 : 1;
|
|
735
|
+
if (valA > valB) return sortAsc ? 1 : -1;
|
|
736
|
+
return 0;
|
|
737
|
+
});
|
|
738
|
+
currentPage = 1;
|
|
739
|
+
renderTable();
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
let activeCat = 'all';
|
|
743
|
+
let searchQuery = '';
|
|
744
|
+
|
|
745
|
+
function filter(cat, btn) {
|
|
746
|
+
document.querySelectorAll('.fbtn').forEach(b=>b.classList.remove('active'));
|
|
747
|
+
if(btn) btn.classList.add('active');
|
|
748
|
+
activeCat = cat;
|
|
749
|
+
applyFilters();
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function search(q) {
|
|
753
|
+
searchQuery = q;
|
|
754
|
+
applyFilters();
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function applyFilters() {
|
|
758
|
+
let regex = null;
|
|
759
|
+
let lq = searchQuery.toLowerCase();
|
|
760
|
+
try { if (searchQuery) regex = new RegExp(searchQuery, 'i'); } catch(e) {}
|
|
761
|
+
|
|
762
|
+
filteredRows = allRows.filter(r => {
|
|
763
|
+
const catMatch = (activeCat === 'all' || r.getAttribute('data-cat') === activeCat);
|
|
764
|
+
if (!catMatch) return false;
|
|
765
|
+
|
|
766
|
+
const txt = r.textContent;
|
|
767
|
+
if (!searchQuery) return true;
|
|
768
|
+
if (regex && regex.test(txt)) return true;
|
|
769
|
+
return txt.toLowerCase().includes(lq);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
currentPage = 1;
|
|
773
|
+
renderTable();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function copyText(el) {
|
|
777
|
+
const text = el.innerText;
|
|
778
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
779
|
+
const toast = document.getElementById('toast');
|
|
780
|
+
toast.classList.add('show');
|
|
781
|
+
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function exportCSV() {
|
|
786
|
+
let csv = "Tailwind Original,AUM-IC Transmutada,Frecuencia,Nivel\\n";
|
|
787
|
+
filteredRows.forEach(r => {
|
|
788
|
+
const cols = r.querySelectorAll('td');
|
|
789
|
+
const orig = cols[0].innerText;
|
|
790
|
+
const aumic = cols[1].innerText;
|
|
791
|
+
const occ = cols[2].querySelector('span').innerText;
|
|
792
|
+
const lvl = cols[3].innerText;
|
|
793
|
+
csv += String.fromCharCode(34) + orig + String.fromCharCode(34) + "," + String.fromCharCode(34) + aumic + String.fromCharCode(34) + "," + occ + "," + String.fromCharCode(34) + lvl + String.fromCharCode(34) + "\\n";
|
|
794
|
+
});
|
|
795
|
+
const blob = new Blob([csv], { type: 'text/csv' });
|
|
796
|
+
const url = window.URL.createObjectURL(blob);
|
|
797
|
+
const a = document.createElement('a');
|
|
798
|
+
a.href = url;
|
|
799
|
+
a.download = 'aumic-report.csv';
|
|
800
|
+
a.click();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function exportMD() {
|
|
804
|
+
let md = "| Tailwind Original | AUM-IC Transmutada | Frecuencia | Nivel |\\n|---|---|---|---|\\n";
|
|
805
|
+
filteredRows.forEach(r => {
|
|
806
|
+
const cols = r.querySelectorAll('td');
|
|
807
|
+
const orig = cols[0].innerText.replace(/\|/g, '|');
|
|
808
|
+
const aumic = cols[1].innerText.replace(/\|/g, '|');
|
|
809
|
+
const occ = cols[2].querySelector('span').innerText;
|
|
810
|
+
const lvl = cols[3].innerText;
|
|
811
|
+
md += "| " + orig + " | " + aumic + " | " + occ + " | " + lvl + " |\\n";
|
|
812
|
+
});
|
|
813
|
+
const blob = new Blob([md], { type: 'text/markdown' });
|
|
814
|
+
const url = window.URL.createObjectURL(blob);
|
|
815
|
+
const a = document.createElement('a');
|
|
816
|
+
a.href = url;
|
|
817
|
+
a.download = 'aumic-report.md';
|
|
818
|
+
a.click();
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
document.addEventListener('keydown', (e) => {
|
|
822
|
+
if (e.key === '/' && document.activeElement.id !== 'searchInput') {
|
|
823
|
+
e.preventDefault();
|
|
824
|
+
document.getElementById('searchInput').focus();
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
|
|
827
828
|
</script></body></html>`;
|
|
828
829
|
fs_extra_1.default.writeFileSync(reportPath, html);
|
|
829
830
|
return reportPath;
|
|
@@ -1010,8 +1011,11 @@ async function run() {
|
|
|
1010
1011
|
const content = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1011
1012
|
try {
|
|
1012
1013
|
const { newCode } = astEngine.processFrameworkFile(content, file, (twClass) => {
|
|
1013
|
-
|
|
1014
|
-
|
|
1014
|
+
// Dividimos por si hay múltiples hashes en el mismo string
|
|
1015
|
+
const classes = twClass.split(" ");
|
|
1016
|
+
const restored = classes.map(c => lockData[c] || c).join(" ");
|
|
1017
|
+
return restored;
|
|
1018
|
+
}, true);
|
|
1015
1019
|
if (newCode !== content) {
|
|
1016
1020
|
await fs_extra_1.default.writeFile(file, newCode, 'utf-8');
|
|
1017
1021
|
restoredCount++;
|
|
@@ -1162,9 +1166,37 @@ async function run() {
|
|
|
1162
1166
|
allUniqueTokens.add(t.trim()); });
|
|
1163
1167
|
}
|
|
1164
1168
|
const testHtml = Array.from(allUniqueTokens).map(t => `<div class="${t}"></div>`).join('\n');
|
|
1169
|
+
const configPath = require('fs').existsSync(require('path').resolve(TARGET_DIR, 'tailwind.config.js')) ? require('path').resolve(TARGET_DIR, 'tailwind.config.js') : (require('fs').existsSync(require('path').resolve(TARGET_DIR, 'tailwind.config.mjs')) ? require('path').resolve(TARGET_DIR, 'tailwind.config.mjs') : null);
|
|
1170
|
+
if (configPath) {
|
|
1171
|
+
try {
|
|
1172
|
+
const dynamicImport = new Function('modulePath', 'return import(modulePath)');
|
|
1173
|
+
userConfig = (await dynamicImport(require('url').pathToFileURL(configPath).href)).default || require(configPath);
|
|
1174
|
+
}
|
|
1175
|
+
catch (e) {
|
|
1176
|
+
try {
|
|
1177
|
+
userConfig = require(configPath);
|
|
1178
|
+
}
|
|
1179
|
+
catch (err) {
|
|
1180
|
+
try {
|
|
1181
|
+
const fsExt = require('fs');
|
|
1182
|
+
const tempPath = configPath + '.mjs';
|
|
1183
|
+
let raw = fsExt.readFileSync(configPath, 'utf8');
|
|
1184
|
+
raw = raw.replace(/module\.exports\s*=\s*/, 'export default ');
|
|
1185
|
+
fsExt.writeFileSync(tempPath, raw, 'utf8');
|
|
1186
|
+
const dynamicImport = new Function('modulePath', 'return import(modulePath)');
|
|
1187
|
+
userConfig = (await dynamicImport(require('url').pathToFileURL(tempPath).href)).default;
|
|
1188
|
+
fsExt.unlinkSync(tempPath);
|
|
1189
|
+
}
|
|
1190
|
+
catch (fatal) {
|
|
1191
|
+
console.log('[AUM-IC] Fallo al cargar Tailwind Config:', fatal.message);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
let mergedConfig = { content: [{ raw: testHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} };
|
|
1165
1197
|
let testPlugin;
|
|
1166
1198
|
try {
|
|
1167
|
-
testPlugin = require('tailwindcss')(
|
|
1199
|
+
testPlugin = require('tailwindcss')(mergedConfig);
|
|
1168
1200
|
}
|
|
1169
1201
|
catch (e) {
|
|
1170
1202
|
testPlugin = require('tailwindcss');
|
|
@@ -1182,6 +1214,36 @@ async function run() {
|
|
|
1182
1214
|
}
|
|
1183
1215
|
});
|
|
1184
1216
|
spinner.succeed(`Filtro JIT completado: ${validTwClasses.size} utilidades nativas reconocidas.`);
|
|
1217
|
+
spinner.start('Analizando entorno y dependencias de Tailwind...');
|
|
1218
|
+
const envStatus = { version: 3, usesAstro: false, usesVite: false, usesPostcss: false, importsPreflight: false, importsUtilities: false, cssEntrypoint: '' };
|
|
1219
|
+
try {
|
|
1220
|
+
const p = require(path_1.default.join(TARGET_DIR, 'package.json'));
|
|
1221
|
+
const deps = { ...(p.dependencies || {}), ...(p.devDependencies || {}) };
|
|
1222
|
+
if (deps['tailwindcss'])
|
|
1223
|
+
envStatus.version = deps['tailwindcss'].startsWith('^4') || deps['tailwindcss'].startsWith('4') ? 4 : 3;
|
|
1224
|
+
else if (deps['@tailwindcss/vite'] || deps['@tailwindcss/postcss'])
|
|
1225
|
+
envStatus.version = 4;
|
|
1226
|
+
envStatus.usesAstro = !!deps['@astrojs/tailwind'];
|
|
1227
|
+
envStatus.usesVite = !!deps['@tailwindcss/vite'];
|
|
1228
|
+
envStatus.usesPostcss = !!deps['@tailwindcss/postcss'];
|
|
1229
|
+
}
|
|
1230
|
+
catch (e) { }
|
|
1231
|
+
const rootCssFiles = await (0, glob_1.glob)('**/*.{css,scss,sass,less,pcss}', { cwd: TARGET_DIR, absolute: true, ignore: ['node_modules/**', 'dist/**', 'public/**'] });
|
|
1232
|
+
for (const file of rootCssFiles) {
|
|
1233
|
+
const c = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1234
|
+
if (c.includes('@tailwind') || c.includes('tailwindcss')) {
|
|
1235
|
+
envStatus.cssEntrypoint = file;
|
|
1236
|
+
if (c.includes('@tailwind base') || c.includes('tailwindcss/preflight') || c.includes('@import "tailwindcss"'))
|
|
1237
|
+
envStatus.importsPreflight = true;
|
|
1238
|
+
if (c.includes('@tailwind utilities') || c.includes('tailwindcss/utilities') || c.includes('@import "tailwindcss"'))
|
|
1239
|
+
envStatus.importsUtilities = true;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (envStatus.usesAstro) {
|
|
1243
|
+
envStatus.importsPreflight = true;
|
|
1244
|
+
envStatus.importsUtilities = true;
|
|
1245
|
+
}
|
|
1246
|
+
spinner.succeed(picocolors_1.default.cyan(`Entorno JIT Dinámico: Tailwind v${envStatus.version} | Preflight: ${envStatus.importsPreflight ? 'Activo' : 'Inactivo'} | Integración: ${envStatus.usesVite ? 'Vite' : envStatus.usesAstro ? 'Astro' : 'PostCSS'}`));
|
|
1185
1247
|
const classMapping = new Map();
|
|
1186
1248
|
let customRules = '';
|
|
1187
1249
|
const rcPath = path_1.default.join(TARGET_DIR, '.aumicrc.json');
|
|
@@ -1368,13 +1430,70 @@ async function run() {
|
|
|
1368
1430
|
spinner.succeed(`CSS extranjero hackeado y reescrito a la doctrina ${aumicLink}.`);
|
|
1369
1431
|
}
|
|
1370
1432
|
else {
|
|
1371
|
-
spinner.start(`Fase 4:
|
|
1372
|
-
|
|
1433
|
+
spinner.start(`Fase 4: Ejecutando L1 Cache (DuckDB) y delegando L2 (JIT v${twVersion})...`);
|
|
1434
|
+
// --- L1 DUCKDB CACHE ---
|
|
1435
|
+
const duckdb = require('duckdb');
|
|
1436
|
+
const dbPath = path_1.default.join(__dirname, '../aumic-lexicon.duckdb');
|
|
1437
|
+
const db = new duckdb.Database(dbPath);
|
|
1438
|
+
let l1Css = '';
|
|
1439
|
+
let unresolvedClasses = new Set();
|
|
1440
|
+
const allTwClasses = new Set();
|
|
1441
|
+
for (const map of classMapping.values()) {
|
|
1442
|
+
map.tailwindOnlyArray.forEach(cls => allTwClasses.add(cls));
|
|
1443
|
+
}
|
|
1444
|
+
const classArray = Array.from(allTwClasses);
|
|
1445
|
+
const l1Promises = classArray.map(cls => {
|
|
1446
|
+
return new Promise((resolve) => {
|
|
1447
|
+
db.all("SELECT css FROM tw_lexicon WHERE version = ? AND class = ?", [twVersion, cls], (err, rows) => {
|
|
1448
|
+
if (err || rows.length === 0) {
|
|
1449
|
+
unresolvedClasses.add(cls);
|
|
1450
|
+
}
|
|
1451
|
+
else {
|
|
1452
|
+
l1Css += `\n/* tw: ${cls} (L1) */\n.${escapeCssSelector(cls)} { ${rows[0].css} }\n`;
|
|
1453
|
+
}
|
|
1454
|
+
resolve();
|
|
1455
|
+
});
|
|
1456
|
+
});
|
|
1457
|
+
});
|
|
1458
|
+
await Promise.all(l1Promises);
|
|
1459
|
+
db.close();
|
|
1460
|
+
// --- L2 DYNAMIC JIT ---
|
|
1461
|
+
const virtualHtml = Array.from(unresolvedClasses).map(cls => `<div class="${cls}"></div>`).join('\n');
|
|
1462
|
+
const projectTwPath = path_1.default.join(TARGET_DIR, 'node_modules', 'tailwindcss');
|
|
1373
1463
|
let twPlugin;
|
|
1374
|
-
|
|
1464
|
+
if (fs_extra_1.default.existsSync(projectTwPath)) {
|
|
1465
|
+
twPlugin = require(projectTwPath)({ content: [{ raw: virtualHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} });
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
twPlugin = require('tailwindcss')({ content: [{ raw: virtualHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} });
|
|
1469
|
+
}
|
|
1375
1470
|
await piscinaPool.destroy();
|
|
1376
1471
|
const jitResult = await (0, postcss_1.default)([twPlugin]).process(`@tailwind utilities;`, { from: undefined });
|
|
1377
|
-
const root = postcss_1.default.parse(jitResult.css);
|
|
1472
|
+
const root = postcss_1.default.parse(l1Css + '\n' + jitResult.css);
|
|
1473
|
+
// =======================================
|
|
1474
|
+
// AUM-IC PREFLIGHT INJECTOR
|
|
1475
|
+
// =======================================
|
|
1476
|
+
let preflightCss = '';
|
|
1477
|
+
if (envStatus.importsPreflight) {
|
|
1478
|
+
try {
|
|
1479
|
+
let pfPlugin;
|
|
1480
|
+
if (fs_extra_1.default.existsSync(projectTwPath)) {
|
|
1481
|
+
pfPlugin = require(projectTwPath)({ content: [{ raw: '<div class="a"></div>', extension: 'html' }], theme: userConfig.theme || {} });
|
|
1482
|
+
}
|
|
1483
|
+
else {
|
|
1484
|
+
pfPlugin = require('tailwindcss')({ content: [{ raw: '<div class="a"></div>', extension: 'html' }], theme: userConfig.theme || {} });
|
|
1485
|
+
}
|
|
1486
|
+
const pfRes = await (0, postcss_1.default)([pfPlugin]).process('@tailwind base;', { from: undefined });
|
|
1487
|
+
preflightCss = pfRes.css;
|
|
1488
|
+
}
|
|
1489
|
+
catch (e) {
|
|
1490
|
+
console.warn("No se pudo extraer Preflight.");
|
|
1491
|
+
}
|
|
1492
|
+
} // end if importsPreflight
|
|
1493
|
+
if (preflightCss) {
|
|
1494
|
+
const pfAst = postcss_1.default.parse(preflightCss);
|
|
1495
|
+
globalRoot.prepend(pfAst);
|
|
1496
|
+
}
|
|
1378
1497
|
root.walkRules((rule) => {
|
|
1379
1498
|
for (const [escapedUtil, targets] of utilityToAumic) {
|
|
1380
1499
|
if (rule.selector.includes(escapedUtil)) {
|
|
@@ -1396,7 +1515,16 @@ async function run() {
|
|
|
1396
1515
|
}
|
|
1397
1516
|
});
|
|
1398
1517
|
if (answers.outputMode === 'global') {
|
|
1399
|
-
|
|
1518
|
+
const outPath = path_1.default.join(TARGET_DIR, 'src', 'styles');
|
|
1519
|
+
require('fs-extra').ensureDirSync(outPath);
|
|
1520
|
+
await fs_extra_1.default.writeFile(path_1.default.join(outPath, 'aumic-styles.css'), themeVars + globalRoot.toString(), 'utf-8');
|
|
1521
|
+
const globalScss = path_1.default.join(outPath, 'global.scss');
|
|
1522
|
+
if (fs_extra_1.default.existsSync(globalScss)) {
|
|
1523
|
+
let scss = fs_extra_1.default.readFileSync(globalScss, 'utf8');
|
|
1524
|
+
if (!scss.includes('aumic-styles')) {
|
|
1525
|
+
fs_extra_1.default.writeFileSync(globalScss, scss + '\n@import "./aumic-styles.css";\n', 'utf8');
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1400
1528
|
}
|
|
1401
1529
|
else {
|
|
1402
1530
|
for (const [cat, cssRoot] of Object.entries(categoryRoots)) {
|
|
@@ -1430,7 +1558,47 @@ async function run() {
|
|
|
1430
1558
|
fs_extra_1.default.renameSync(confPath, `${confPath}.aumic-bak`);
|
|
1431
1559
|
}
|
|
1432
1560
|
});
|
|
1433
|
-
|
|
1561
|
+
// ==========================================
|
|
1562
|
+
// UNIVERSAL TAILWIND ERADICATOR (v1 - v4)
|
|
1563
|
+
// ==========================================
|
|
1564
|
+
// 1. Respaldar y neutralizar archivos de configuración (v1, v2, v3)
|
|
1565
|
+
const twConfigFiles = ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs', 'tailwind.config.mjs', 'tailwind.js'];
|
|
1566
|
+
twConfigFiles.forEach(conf => {
|
|
1567
|
+
const confPath = require('path').join(TARGET_DIR, conf);
|
|
1568
|
+
if (fs_extra_1.default.existsSync(confPath))
|
|
1569
|
+
fs_extra_1.default.renameSync(confPath, `${confPath}.aumic-bak`);
|
|
1570
|
+
});
|
|
1571
|
+
// 2. Limpieza de Integradores (v4 Vite, Astro, PostCSS)
|
|
1572
|
+
const integrations = [
|
|
1573
|
+
{ file: 'astro.config.mjs', regex: /import\s+tailwind\s+from\s+['"]@astrojs\/tailwind['"];?\n?/, regex2: /tailwind\(\)\s*,?/ },
|
|
1574
|
+
{ file: 'astro.config.ts', regex: /import\s+tailwind\s+from\s+['"]@astrojs\/tailwind['"];?\n?/, regex2: /tailwind\(\)\s*,?/ },
|
|
1575
|
+
{ file: 'vite.config.js', regex: /import\s+tailwindcss\s+from\s+['"]@tailwindcss\/vite['"];?\n?/, regex2: /tailwindcss\(\)\s*,?/ },
|
|
1576
|
+
{ file: 'vite.config.ts', regex: /import\s+tailwindcss\s+from\s+['"]@tailwindcss\/vite['"];?\n?/, regex2: /tailwindcss\(\)\s*,?/ }
|
|
1577
|
+
];
|
|
1578
|
+
integrations.forEach(intg => {
|
|
1579
|
+
const intgPath = require('path').join(TARGET_DIR, intg.file);
|
|
1580
|
+
if (fs_extra_1.default.existsSync(intgPath)) {
|
|
1581
|
+
let c = fs_extra_1.default.readFileSync(intgPath, 'utf8');
|
|
1582
|
+
c = c.replace(intg.regex, '');
|
|
1583
|
+
c = c.replace(intg.regex2, '');
|
|
1584
|
+
fs_extra_1.default.writeFileSync(intgPath, c, 'utf8');
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
// 3. Desinstalación Dinámica Universal (Identifica dependencias exactas en package.json)
|
|
1588
|
+
try {
|
|
1589
|
+
const pkg = require(require('path').join(TARGET_DIR, 'package.json'));
|
|
1590
|
+
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
1591
|
+
const targets = ['tailwindcss', '@tailwindcss/vite', '@tailwindcss/postcss', '@tailwindcss/cli', '@astrojs/tailwind'];
|
|
1592
|
+
// Si encontramos tailwind v1 o v2, muchas veces se usaba autoprefixer a la par estrictamente.
|
|
1593
|
+
if (allDeps['tailwindcss'] && (allDeps['tailwindcss'].startsWith('^1') || allDeps['tailwindcss'].startsWith('^2'))) {
|
|
1594
|
+
// Solo como heurÃstica
|
|
1595
|
+
}
|
|
1596
|
+
const toUninstall = targets.filter(t => allDeps[t]);
|
|
1597
|
+
if (toUninstall.length > 0) {
|
|
1598
|
+
require('child_process').execSync(`npm uninstall ${toUninstall.join(' ')}`, { cwd: TARGET_DIR, stdio: 'ignore' });
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
catch (e) { }
|
|
1434
1602
|
spinner.succeed(picocolors_1.default.green('Tailwind ha sido purgado completamente. (Configuraciones y package.json respaldados en .aumic-bak)'));
|
|
1435
1603
|
}
|
|
1436
1604
|
catch (e) {
|
|
@@ -1439,7 +1607,7 @@ async function run() {
|
|
|
1439
1607
|
}
|
|
1440
1608
|
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
1441
1609
|
logAudit(`[EJECUCIÓN] Operación completada exitosamente en ${duration} segundos.`);
|
|
1442
|
-
console.log(picocolors_1.default.green(picocolors_1.default.bold(`\n[✔] PROYECTO TRANSMUTADO A LA DOCTRINA ${aumicLink}. (
|
|
1610
|
+
console.log(picocolors_1.default.green(picocolors_1.default.bold(`\n[✔] PROYECTO TRANSMUTADO A LA DOCTRINA ${aumicLink}. (v${pkgVersion})\n`)));
|
|
1443
1611
|
// Generate Final Report
|
|
1444
1612
|
spinner.start('Generando reporte post-mortem...');
|
|
1445
1613
|
const postReportPath = generateReport(classMapping, TARGET_DIR, false);
|