@navinjoseph/web-perf-core 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/audits/accessibility.d.ts +10 -0
  2. package/dist/audits/accessibility.d.ts.map +1 -0
  3. package/dist/audits/accessibility.js +258 -0
  4. package/dist/audits/accessibility.js.map +1 -0
  5. package/dist/audits/best-practices.d.ts +10 -0
  6. package/dist/audits/best-practices.d.ts.map +1 -0
  7. package/dist/audits/best-practices.js +696 -0
  8. package/dist/audits/best-practices.js.map +1 -0
  9. package/dist/audits/constants.d.ts +8 -0
  10. package/dist/audits/constants.d.ts.map +1 -0
  11. package/dist/audits/constants.js +278 -0
  12. package/dist/audits/constants.js.map +1 -0
  13. package/dist/audits/performance.d.ts +11 -0
  14. package/dist/audits/performance.d.ts.map +1 -0
  15. package/dist/audits/performance.js +497 -0
  16. package/dist/audits/performance.js.map +1 -0
  17. package/dist/audits/pwa.d.ts +10 -0
  18. package/dist/audits/pwa.d.ts.map +1 -0
  19. package/dist/audits/pwa.js +396 -0
  20. package/dist/audits/pwa.js.map +1 -0
  21. package/dist/audits/security.d.ts +10 -0
  22. package/dist/audits/security.d.ts.map +1 -0
  23. package/dist/audits/security.js +249 -0
  24. package/dist/audits/security.js.map +1 -0
  25. package/dist/audits/seo.d.ts +10 -0
  26. package/dist/audits/seo.d.ts.map +1 -0
  27. package/dist/audits/seo.js +471 -0
  28. package/dist/audits/seo.js.map +1 -0
  29. package/dist/browser.d.ts +21 -0
  30. package/dist/browser.d.ts.map +1 -0
  31. package/dist/browser.js +178 -0
  32. package/dist/browser.js.map +1 -0
  33. package/dist/dom-collector.d.ts +45 -0
  34. package/dist/dom-collector.d.ts.map +1 -0
  35. package/dist/dom-collector.js +173 -0
  36. package/dist/dom-collector.js.map +1 -0
  37. package/dist/formatter.d.ts +60 -0
  38. package/dist/formatter.d.ts.map +1 -0
  39. package/dist/formatter.js +164 -0
  40. package/dist/formatter.js.map +1 -0
  41. package/dist/id-generator.d.ts +22 -0
  42. package/dist/id-generator.d.ts.map +1 -0
  43. package/dist/id-generator.js +29 -0
  44. package/dist/id-generator.js.map +1 -0
  45. package/dist/index.d.ts +17 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +23 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/orchestrator.d.ts +41 -0
  50. package/dist/orchestrator.d.ts.map +1 -0
  51. package/dist/orchestrator.js +194 -0
  52. package/dist/orchestrator.js.map +1 -0
  53. package/dist/reporters/html.d.ts +6 -0
  54. package/dist/reporters/html.d.ts.map +1 -0
  55. package/dist/reporters/html.js +688 -0
  56. package/dist/reporters/html.js.map +1 -0
  57. package/dist/reporters/index.d.ts +4 -0
  58. package/dist/reporters/index.d.ts.map +1 -0
  59. package/dist/reporters/index.js +17 -0
  60. package/dist/reporters/index.js.map +1 -0
  61. package/dist/reporters/json.d.ts +6 -0
  62. package/dist/reporters/json.d.ts.map +1 -0
  63. package/dist/reporters/json.js +7 -0
  64. package/dist/reporters/json.js.map +1 -0
  65. package/dist/reporters/terminal.d.ts +6 -0
  66. package/dist/reporters/terminal.d.ts.map +1 -0
  67. package/dist/reporters/terminal.js +180 -0
  68. package/dist/reporters/terminal.js.map +1 -0
  69. package/dist/reporters/types.d.ts +2 -0
  70. package/dist/reporters/types.d.ts.map +1 -0
  71. package/dist/reporters/types.js +2 -0
  72. package/dist/reporters/types.js.map +1 -0
  73. package/dist/types.d.ts +80 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +36 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +54 -0
@@ -0,0 +1,688 @@
1
+ /**
2
+ * Render audit report as an interactive HTML dashboard
3
+ */
4
+ export function renderHtmlReport(report) {
5
+ const issuesJson = JSON.stringify(report.issues);
6
+ return `<!DOCTYPE html>
7
+ <html lang="en">
8
+ <head>
9
+ <meta charset="UTF-8">
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
+ <title>web-perf Audit Report - ${report.url}</title>
12
+ <style>
13
+ * {
14
+ margin: 0;
15
+ padding: 0;
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ body {
20
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
21
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
+ padding: 2rem;
23
+ color: #333;
24
+ }
25
+
26
+ .container {
27
+ max-width: 1400px;
28
+ margin: 0 auto;
29
+ background: white;
30
+ border-radius: 12px;
31
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
32
+ overflow: hidden;
33
+ }
34
+
35
+ .header {
36
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
37
+ color: white;
38
+ padding: 2rem 2.5rem;
39
+ }
40
+
41
+ .header h1 {
42
+ font-size: 2.5rem;
43
+ margin-bottom: 0.5rem;
44
+ display: flex;
45
+ align-items: center;
46
+ gap: 1rem;
47
+ }
48
+
49
+ .header-info {
50
+ opacity: 0.95;
51
+ font-size: 0.95rem;
52
+ display: grid;
53
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
54
+ gap: 1rem;
55
+ margin-top: 1.5rem;
56
+ }
57
+
58
+ .header-info-item {
59
+ background: rgba(255, 255, 255, 0.1);
60
+ padding: 0.75rem 1rem;
61
+ border-radius: 6px;
62
+ backdrop-filter: blur(10px);
63
+ }
64
+
65
+ .header-info-label {
66
+ font-size: 0.8rem;
67
+ opacity: 0.8;
68
+ margin-bottom: 0.25rem;
69
+ }
70
+
71
+ .header-info-value {
72
+ font-size: 1.1rem;
73
+ font-weight: 600;
74
+ }
75
+
76
+ .content {
77
+ padding: 2.5rem;
78
+ }
79
+
80
+ .summary {
81
+ display: grid;
82
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
83
+ gap: 1.5rem;
84
+ margin-bottom: 2.5rem;
85
+ }
86
+
87
+ .summary-card {
88
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
89
+ padding: 1.5rem;
90
+ border-radius: 10px;
91
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
92
+ }
93
+
94
+ .summary-card h3 {
95
+ font-size: 0.9rem;
96
+ text-transform: uppercase;
97
+ letter-spacing: 1px;
98
+ margin-bottom: 1rem;
99
+ color: #555;
100
+ }
101
+
102
+ .severity-grid, .category-grid {
103
+ display: grid;
104
+ gap: 0.5rem;
105
+ }
106
+
107
+ .severity-item, .category-item {
108
+ display: flex;
109
+ justify-content: space-between;
110
+ align-items: center;
111
+ padding: 0.5rem 0.75rem;
112
+ background: white;
113
+ border-radius: 6px;
114
+ border-left: 4px solid #ccc;
115
+ }
116
+
117
+ .severity-item.critical { border-left-color: #d32f2f; }
118
+ .severity-item.serious { border-left-color: #f57c00; }
119
+ .severity-item.moderate { border-left-color: #7b1fa2; }
120
+ .severity-item.minor { border-left-color: #0288d1; }
121
+
122
+ .severity-item .badge {
123
+ background: #eee;
124
+ padding: 0.25rem 0.75rem;
125
+ border-radius: 12px;
126
+ font-weight: 600;
127
+ font-size: 0.9rem;
128
+ }
129
+
130
+ .severity-item.critical .badge { background: #ffcdd2; color: #d32f2f; }
131
+ .severity-item.serious .badge { background: #ffe0b2; color: #f57c00; }
132
+ .severity-item.moderate .badge { background: #e1bee7; color: #7b1fa2; }
133
+ .severity-item.minor .badge { background: #b3e5fc; color: #0288d1; }
134
+
135
+ .filters {
136
+ background: #f8f9fa;
137
+ padding: 1.5rem;
138
+ border-radius: 10px;
139
+ margin-bottom: 2rem;
140
+ display: flex;
141
+ flex-wrap: wrap;
142
+ gap: 1rem;
143
+ align-items: center;
144
+ }
145
+
146
+ .filter-group {
147
+ display: flex;
148
+ gap: 0.5rem;
149
+ align-items: center;
150
+ }
151
+
152
+ .filter-label {
153
+ font-weight: 600;
154
+ font-size: 0.9rem;
155
+ color: #555;
156
+ }
157
+
158
+ .filter-btn {
159
+ padding: 0.5rem 1rem;
160
+ border: 2px solid #ddd;
161
+ background: white;
162
+ border-radius: 6px;
163
+ cursor: pointer;
164
+ font-size: 0.85rem;
165
+ transition: all 0.2s;
166
+ }
167
+
168
+ .filter-btn:hover {
169
+ border-color: #667eea;
170
+ background: #f0f4ff;
171
+ }
172
+
173
+ .filter-btn.active {
174
+ background: #667eea;
175
+ color: white;
176
+ border-color: #667eea;
177
+ }
178
+
179
+ .search-box {
180
+ flex: 1;
181
+ min-width: 250px;
182
+ display: flex;
183
+ align-items: center;
184
+ gap: 0.5rem;
185
+ }
186
+
187
+ .search-box input {
188
+ flex: 1;
189
+ padding: 0.75rem;
190
+ border: 2px solid #ddd;
191
+ border-radius: 6px;
192
+ font-size: 0.9rem;
193
+ }
194
+
195
+ .search-box input:focus {
196
+ outline: none;
197
+ border-color: #667eea;
198
+ }
199
+
200
+ .issues-container {
201
+ display: flex;
202
+ flex-direction: column;
203
+ gap: 1.5rem;
204
+ }
205
+
206
+ .issue-card {
207
+ background: white;
208
+ border: 2px solid #e0e0e0;
209
+ border-radius: 10px;
210
+ padding: 1.5rem;
211
+ transition: all 0.3s;
212
+ border-left: 6px solid #ccc;
213
+ }
214
+
215
+ .issue-card:hover {
216
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
217
+ transform: translateY(-2px);
218
+ }
219
+
220
+ .issue-card.critical { border-left-color: #d32f2f; }
221
+ .issue-card.serious { border-left-color: #f57c00; }
222
+ .issue-card.moderate { border-left-color: #7b1fa2; }
223
+ .issue-card.minor { border-left-color: #0288d1; }
224
+
225
+ .issue-header {
226
+ display: flex;
227
+ justify-content: space-between;
228
+ align-items: flex-start;
229
+ margin-bottom: 1rem;
230
+ gap: 1rem;
231
+ }
232
+
233
+ .issue-title {
234
+ flex: 1;
235
+ font-size: 1.1rem;
236
+ font-weight: 600;
237
+ color: #333;
238
+ }
239
+
240
+ .issue-badges {
241
+ display: flex;
242
+ gap: 0.5rem;
243
+ flex-wrap: wrap;
244
+ }
245
+
246
+ .badge {
247
+ padding: 0.25rem 0.75rem;
248
+ border-radius: 12px;
249
+ font-size: 0.8rem;
250
+ font-weight: 600;
251
+ text-transform: uppercase;
252
+ }
253
+
254
+ .badge.critical { background: #ffcdd2; color: #d32f2f; }
255
+ .badge.serious { background: #ffe0b2; color: #f57c00; }
256
+ .badge.moderate { background: #e1bee7; color: #7b1fa2; }
257
+ .badge.minor { background: #b3e5fc; color: #0288d1; }
258
+ .badge.category { background: #e8eaf6; color: #3f51b5; }
259
+
260
+ .issue-description {
261
+ color: #666;
262
+ margin-bottom: 1rem;
263
+ line-height: 1.6;
264
+ }
265
+
266
+ .issue-section {
267
+ margin-top: 1rem;
268
+ padding-top: 1rem;
269
+ border-top: 1px solid #eee;
270
+ }
271
+
272
+ .issue-section-title {
273
+ font-weight: 600;
274
+ font-size: 0.9rem;
275
+ color: #555;
276
+ margin-bottom: 0.5rem;
277
+ display: flex;
278
+ align-items: center;
279
+ gap: 0.5rem;
280
+ }
281
+
282
+ .element-info {
283
+ background: #f5f5f5;
284
+ padding: 1rem;
285
+ border-radius: 6px;
286
+ font-size: 0.9rem;
287
+ }
288
+
289
+ .element-selector {
290
+ font-family: 'Courier New', monospace;
291
+ color: #0288d1;
292
+ background: white;
293
+ padding: 0.5rem;
294
+ border-radius: 4px;
295
+ margin: 0.5rem 0;
296
+ word-break: break-all;
297
+ }
298
+
299
+ .element-html {
300
+ font-family: 'Courier New', monospace;
301
+ color: #666;
302
+ background: white;
303
+ padding: 0.5rem;
304
+ border-radius: 4px;
305
+ margin: 0.5rem 0;
306
+ font-size: 0.85rem;
307
+ overflow-x: auto;
308
+ white-space: pre-wrap;
309
+ word-break: break-all;
310
+ }
311
+
312
+ .wcag-info {
313
+ background: #e3f2fd;
314
+ padding: 1rem;
315
+ border-radius: 6px;
316
+ font-size: 0.9rem;
317
+ }
318
+
319
+ .fix-guidance {
320
+ background: #e8f5e9;
321
+ padding: 1rem;
322
+ border-radius: 6px;
323
+ }
324
+
325
+ .fix-description {
326
+ color: #2e7d32;
327
+ margin-bottom: 0.75rem;
328
+ font-weight: 500;
329
+ }
330
+
331
+ .code-block {
332
+ background: #263238;
333
+ color: #aed581;
334
+ padding: 1rem;
335
+ border-radius: 6px;
336
+ font-family: 'Courier New', monospace;
337
+ font-size: 0.85rem;
338
+ overflow-x: auto;
339
+ margin: 0.5rem 0;
340
+ line-height: 1.5;
341
+ white-space: pre-wrap;
342
+ }
343
+
344
+ .learn-more {
345
+ margin-top: 0.75rem;
346
+ font-size: 0.9rem;
347
+ }
348
+
349
+ .learn-more a {
350
+ color: #1976d2;
351
+ text-decoration: none;
352
+ font-weight: 500;
353
+ }
354
+
355
+ .learn-more a:hover {
356
+ text-decoration: underline;
357
+ }
358
+
359
+ .passed-section {
360
+ margin-top: 2rem;
361
+ padding: 1.5rem;
362
+ background: #f1f8f4;
363
+ border-radius: 10px;
364
+ border-left: 6px solid #4caf50;
365
+ }
366
+
367
+ .passed-section h2 {
368
+ color: #2e7d32;
369
+ margin-bottom: 1rem;
370
+ display: flex;
371
+ align-items: center;
372
+ gap: 0.5rem;
373
+ }
374
+
375
+ .passed-grid {
376
+ display: grid;
377
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
378
+ gap: 0.75rem;
379
+ }
380
+
381
+ .passed-item {
382
+ background: white;
383
+ padding: 0.75rem 1rem;
384
+ border-radius: 6px;
385
+ display: flex;
386
+ align-items: center;
387
+ gap: 0.5rem;
388
+ font-size: 0.9rem;
389
+ }
390
+
391
+ .no-issues {
392
+ text-align: center;
393
+ padding: 4rem 2rem;
394
+ font-size: 1.5rem;
395
+ color: #4caf50;
396
+ }
397
+
398
+ .no-results {
399
+ text-align: center;
400
+ padding: 3rem;
401
+ color: #999;
402
+ font-size: 1.1rem;
403
+ }
404
+
405
+ @media (max-width: 768px) {
406
+ body {
407
+ padding: 1rem;
408
+ }
409
+
410
+ .header h1 {
411
+ font-size: 1.8rem;
412
+ }
413
+
414
+ .content {
415
+ padding: 1.5rem;
416
+ }
417
+
418
+ .summary {
419
+ grid-template-columns: 1fr;
420
+ }
421
+
422
+ .filters {
423
+ flex-direction: column;
424
+ align-items: stretch;
425
+ }
426
+
427
+ .filter-group {
428
+ flex-direction: column;
429
+ align-items: stretch;
430
+ }
431
+ }
432
+ </style>
433
+ </head>
434
+ <body>
435
+ <div class="container">
436
+ <div class="header">
437
+ <h1>🔍 web-perf Audit Report</h1>
438
+ <div class="header-info">
439
+ <div class="header-info-item">
440
+ <div class="header-info-label">URL</div>
441
+ <div class="header-info-value" style="font-size: 0.9rem; word-break: break-all;">${report.url}</div>
442
+ </div>
443
+ <div class="header-info-item">
444
+ <div class="header-info-label">Timestamp</div>
445
+ <div class="header-info-value">${new Date(report.timestamp).toLocaleString()}</div>
446
+ </div>
447
+ <div class="header-info-item">
448
+ <div class="header-info-label">Duration</div>
449
+ <div class="header-info-value">${report.duration}ms</div>
450
+ </div>
451
+ <div class="header-info-item">
452
+ <div class="header-info-label">Total Issues</div>
453
+ <div class="header-info-value">${report.summary.total}</div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <div class="content">
459
+ <div class="summary">
460
+ <div class="summary-card">
461
+ <h3>📊 Severity Breakdown</h3>
462
+ <div class="severity-grid">
463
+ ${Object.entries(report.summary.bySeverity)
464
+ .map(([sev, count]) => `
465
+ <div class="severity-item ${sev}">
466
+ <span>${sev.charAt(0).toUpperCase() + sev.slice(1)}</span>
467
+ <span class="badge">${count}</span>
468
+ </div>
469
+ `)
470
+ .join("")}
471
+ </div>
472
+ </div>
473
+
474
+ <div class="summary-card">
475
+ <h3>📁 Category Breakdown</h3>
476
+ <div class="category-grid">
477
+ ${Object.entries(report.summary.byCategory)
478
+ .filter(([_, count]) => count > 0)
479
+ .map(([cat, count]) => `
480
+ <div class="category-item">
481
+ <span>${cat.charAt(0).toUpperCase() + cat.slice(1)}</span>
482
+ <span class="badge">${count}</span>
483
+ </div>
484
+ `)
485
+ .join("")}
486
+ </div>
487
+ </div>
488
+ </div>
489
+
490
+ ${report.issues.length > 0
491
+ ? `
492
+ <div class="filters">
493
+ <div class="filter-group">
494
+ <span class="filter-label">Severity:</span>
495
+ <button class="filter-btn active" data-severity="all">All</button>
496
+ <button class="filter-btn" data-severity="critical">Critical</button>
497
+ <button class="filter-btn" data-severity="serious">Serious</button>
498
+ <button class="filter-btn" data-severity="moderate">Moderate</button>
499
+ <button class="filter-btn" data-severity="minor">Minor</button>
500
+ </div>
501
+ <div class="filter-group">
502
+ <span class="filter-label">Category:</span>
503
+ <button class="filter-btn active" data-category="all">All</button>
504
+ ${Array.from(new Set(report.issues.map((i) => i.category)))
505
+ .map((cat) => `<button class="filter-btn" data-category="${cat}">${cat.charAt(0).toUpperCase() + cat.slice(1)}</button>`)
506
+ .join("")}
507
+ </div>
508
+ <div class="search-box">
509
+ <span class="filter-label">🔍</span>
510
+ <input type="text" id="searchInput" placeholder="Search issues...">
511
+ </div>
512
+ </div>
513
+
514
+ <div class="issues-container" id="issuesContainer"></div>
515
+ <div class="no-results" id="noResults" style="display: none;">
516
+ No issues match your filters
517
+ </div>
518
+ `
519
+ : `
520
+ <div class="no-issues">
521
+ 🎉 No issues found! Your website passed all checks.
522
+ </div>
523
+ `}
524
+
525
+ ${report.passed.length > 0
526
+ ? `
527
+ <div class="passed-section">
528
+ <h2>✅ Passed Checks (${report.passed.length})</h2>
529
+ <div class="passed-grid">
530
+ ${report.passed
531
+ .map((check) => `
532
+ <div class="passed-item">
533
+ <span style="color: #4caf50;">✓</span>
534
+ <span>${check.name}</span>
535
+ </div>
536
+ `)
537
+ .join("")}
538
+ </div>
539
+ </div>
540
+ `
541
+ : ""}
542
+ </div>
543
+ </div>
544
+
545
+ <script>
546
+ const issues = ${issuesJson};
547
+ let currentFilters = { severity: 'all', category: 'all', search: '' };
548
+
549
+ function renderIssues() {
550
+ const container = document.getElementById('issuesContainer');
551
+ const noResults = document.getElementById('noResults');
552
+
553
+ const filteredIssues = issues.filter(issue => {
554
+ const matchesSeverity = currentFilters.severity === 'all' || issue.severity === currentFilters.severity;
555
+ const matchesCategory = currentFilters.category === 'all' || issue.category === currentFilters.category;
556
+ const matchesSearch = !currentFilters.search ||
557
+ issue.message.toLowerCase().includes(currentFilters.search.toLowerCase()) ||
558
+ issue.description.toLowerCase().includes(currentFilters.search.toLowerCase()) ||
559
+ issue.ruleId.toLowerCase().includes(currentFilters.search.toLowerCase());
560
+
561
+ return matchesSeverity && matchesCategory && matchesSearch;
562
+ });
563
+
564
+ if (filteredIssues.length === 0) {
565
+ container.style.display = 'none';
566
+ noResults.style.display = 'block';
567
+ return;
568
+ }
569
+
570
+ container.style.display = 'flex';
571
+ noResults.style.display = 'none';
572
+
573
+ container.innerHTML = filteredIssues.map((issue, index) => {
574
+ let html = \`
575
+ <div class="issue-card \${issue.severity}">
576
+ <div class="issue-header">
577
+ <div class="issue-title">\${issue.message}</div>
578
+ <div class="issue-badges">
579
+ <span class="badge \${issue.severity}">\${issue.severity}</span>
580
+ <span class="badge category">\${issue.category}</span>
581
+ </div>
582
+ </div>
583
+ <div class="issue-description">\${issue.description || ''}</div>
584
+ \`;
585
+
586
+ // Element information
587
+ if (issue.element) {
588
+ html += \`
589
+ <div class="issue-section">
590
+ <div class="issue-section-title">🎯 Element Information</div>
591
+ <div class="element-info">
592
+ <div><strong>Selector:</strong></div>
593
+ <div class="element-selector">\${issue.element.selector}</div>
594
+ \${issue.element.html ? \`
595
+ <div style="margin-top: 0.75rem;"><strong>HTML:</strong></div>
596
+ <div class="element-html">\${escapeHtml(issue.element.html)}</div>
597
+ \` : ''}
598
+ \${issue.element.failureSummary ? \`
599
+ <div style="margin-top: 0.75rem;"><strong>Failure:</strong> \${issue.element.failureSummary}</div>
600
+ \` : ''}
601
+ </div>
602
+ </div>
603
+ \`;
604
+ }
605
+
606
+ // WCAG information
607
+ if (issue.wcag && issue.wcag.id !== 'N/A' && issue.wcag.id !== 'Unknown') {
608
+ html += \`
609
+ <div class="issue-section">
610
+ <div class="issue-section-title">📋 WCAG Guideline</div>
611
+ <div class="wcag-info">
612
+ <strong>WCAG \${issue.wcag.id}</strong> (Level \${issue.wcag.level})<br>
613
+ \${issue.wcag.name}<br>
614
+ <small style="color: #666;">\${issue.wcag.description}</small>
615
+ </div>
616
+ </div>
617
+ \`;
618
+ }
619
+
620
+ // Fix guidance
621
+ if (issue.fix) {
622
+ html += \`
623
+ <div class="issue-section">
624
+ <div class="issue-section-title">🔧 Fix Guidance</div>
625
+ <div class="fix-guidance">
626
+ <div class="fix-description">\${issue.fix.description}</div>
627
+ \${issue.fix.code ? \`
628
+ <div><strong>Code Example:</strong></div>
629
+ <div class="code-block">\${escapeHtml(issue.fix.code)}</div>
630
+ \` : ''}
631
+ \${issue.fix.learnMoreUrl ? \`
632
+ <div class="learn-more">
633
+ <a href="\${issue.fix.learnMoreUrl}" target="_blank" rel="noopener">Learn more →</a>
634
+ </div>
635
+ \` : ''}
636
+ </div>
637
+ </div>
638
+ \`;
639
+ }
640
+
641
+ html += '</div>';
642
+ return html;
643
+ }).join('');
644
+ }
645
+
646
+ function escapeHtml(text) {
647
+ const div = document.createElement('div');
648
+ div.textContent = text;
649
+ return div.innerHTML;
650
+ }
651
+
652
+ // Filter buttons
653
+ document.querySelectorAll('[data-severity]').forEach(btn => {
654
+ btn.addEventListener('click', () => {
655
+ document.querySelectorAll('[data-severity]').forEach(b => b.classList.remove('active'));
656
+ btn.classList.add('active');
657
+ currentFilters.severity = btn.dataset.severity;
658
+ renderIssues();
659
+ });
660
+ });
661
+
662
+ document.querySelectorAll('[data-category]').forEach(btn => {
663
+ btn.addEventListener('click', () => {
664
+ document.querySelectorAll('[data-category]').forEach(b => b.classList.remove('active'));
665
+ btn.classList.add('active');
666
+ currentFilters.category = btn.dataset.category;
667
+ renderIssues();
668
+ });
669
+ });
670
+
671
+ // Search
672
+ const searchInput = document.getElementById('searchInput');
673
+ if (searchInput) {
674
+ searchInput.addEventListener('input', (e) => {
675
+ currentFilters.search = e.target.value;
676
+ renderIssues();
677
+ });
678
+ }
679
+
680
+ // Initial render
681
+ if (issues.length > 0) {
682
+ renderIssues();
683
+ }
684
+ </script>
685
+ </body>
686
+ </html>`;
687
+ }
688
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/reporters/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { AuditReport, ThresholdConfig } from "../types.js";
2
+ import { ReporterFormat } from "./types.js";
3
+ export declare function renderReport(report: AuditReport, format: ReporterFormat, threshold?: ThresholdConfig): string | void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI3D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,cAAc,EACtB,SAAS,CAAC,EAAE,eAAe,GAC1B,MAAM,GAAG,IAAI,CAYf"}