@ai-lighthouse/cli 1.0.1 โ†’ 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/index.js +2573 -12
  2. package/package.json +10 -4
  3. package/.ai-lighthouse/audit_example.com_2025-12-15T12-10-43.json +0 -183
  4. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-32-28.html +0 -743
  5. package/.ai-lighthouse/audit_fayeed.dev_2026-01-07T19-33-02.html +0 -757
  6. package/.ai-lighthouse/audit_github.com_2025-12-15T11-53-21.json +0 -168
  7. package/.ai-lighthouse/audit_github.com_2025-12-15T12-04-06.json +0 -168
  8. package/.ai-lighthouse/audit_github.com_2025-12-15T12-05-10.json +0 -168
  9. package/.ai-lighthouse/audit_github.com_2025-12-15T12-09-45.json +0 -168
  10. package/.ai-lighthouse/audit_github.com_2025-12-15T12-11-07.json +0 -168
  11. package/.ai-lighthouse/audit_github.com_2025-12-15T12-13-28.json +0 -168
  12. package/.ai-lighthouse/audit_github.com_2025-12-15T12-14-59.json +0 -205
  13. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-07.json +0 -205
  14. package/.ai-lighthouse/audit_github.com_2025-12-15T12-18-44.json +0 -205
  15. package/.ai-lighthouse/audit_github.com_2025-12-15T12-21-38.json +0 -205
  16. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-21.json +0 -205
  17. package/.ai-lighthouse/audit_github.com_2025-12-15T12-22-46.json +0 -205
  18. package/.ai-lighthouse/audit_github.com_2025-12-15T12-23-18.json +0 -205
  19. package/.ai-lighthouse/audit_github.com_2025-12-15T12-24-43.json +0 -205
  20. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-08.json +0 -168
  21. package/.ai-lighthouse/audit_github.com_2025-12-17T12-15-57.json +0 -168
  22. package/.ai-lighthouse/audit_github.com_2025-12-17T12-17-11.json +0 -168
  23. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-17.json +0 -168
  24. package/.ai-lighthouse/audit_github.com_2025-12-17T12-22-42.json +0 -168
  25. package/.ai-lighthouse/audit_github.com_2025-12-17T12-23-56.json +0 -168
  26. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-24.json +0 -168
  27. package/.ai-lighthouse/audit_github.com_2025-12-17T12-25-40.json +0 -168
  28. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-02.json +0 -168
  29. package/.ai-lighthouse/audit_github.com_2025-12-17T12-27-20.json +0 -168
  30. package/.ai-lighthouse/audit_github.com_2025-12-17T12-29-56.json +0 -168
  31. package/.ai-lighthouse/audit_github.com_2025-12-17T12-32-27.json +0 -168
  32. package/.ai-lighthouse/audit_github.com_2025-12-17T12-33-00.json +0 -168
  33. package/.ai-lighthouse/audit_github.com_2025-12-17T12-34-49.json +0 -168
  34. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-31.json +0 -168
  35. package/.ai-lighthouse/audit_stripe.com_2025-12-15T12-11-45.json +0 -168
  36. package/.ai-lighthouse/audit_tailwindcss.com_2025-12-15T12-12-01.json +0 -169
  37. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-08.json +0 -24
  38. package/.ai-lighthouse/crawl_example.com_2025-12-15T12-03-23.json +0 -24
  39. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-41-34.json +0 -21
  40. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-09.json +0 -21
  41. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-42-45.json +0 -21
  42. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-02.json +0 -21
  43. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-43-26.json +0 -21
  44. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-47-46.json +0 -906
  45. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-50-27.json +0 -906
  46. package/.ai-lighthouse/crawl_github.com_2025-12-15T11-52-59.json +0 -906
  47. package/.ai-lighthouse/crawl_github.com_2025-12-15T12-03-33.json +0 -28
  48. package/CLI_UI_README.md +0 -211
  49. package/EXAMPLES.md +0 -87
  50. package/IMPLEMENTATION.md +0 -215
  51. package/USAGE.md +0 -264
  52. package/WIZARD_GUIDE.md +0 -340
  53. package/bin/cli.js +0 -2
  54. package/dist/commands/audit-interactive.d.ts +0 -2
  55. package/dist/commands/audit-interactive.js +0 -106
  56. package/dist/commands/audit-wizard.d.ts +0 -2
  57. package/dist/commands/audit-wizard.js +0 -110
  58. package/dist/commands/audit.d.ts +0 -2
  59. package/dist/commands/audit.js +0 -940
  60. package/dist/commands/crawl.d.ts +0 -2
  61. package/dist/commands/crawl.js +0 -267
  62. package/dist/commands/report.d.ts +0 -2
  63. package/dist/commands/report.js +0 -304
  64. package/dist/index.d.ts +0 -1
  65. package/dist/ui/AuditReportUI.d.ts +0 -10
  66. package/dist/ui/AuditReportUI.js +0 -76
  67. package/dist/ui/SetupWizard.d.ts +0 -18
  68. package/dist/ui/SetupWizard.js +0 -179
  69. package/dist/ui/components/AIUnderstandingSection.d.ts +0 -6
  70. package/dist/ui/components/AIUnderstandingSection.js +0 -87
  71. package/dist/ui/components/HallucinationSection.d.ts +0 -6
  72. package/dist/ui/components/HallucinationSection.js +0 -84
  73. package/dist/ui/components/IssuesSection.d.ts +0 -6
  74. package/dist/ui/components/IssuesSection.js +0 -84
  75. package/dist/ui/components/MessageAlignmentSection.d.ts +0 -6
  76. package/dist/ui/components/MessageAlignmentSection.js +0 -108
  77. package/dist/ui/components/OverviewSection.d.ts +0 -6
  78. package/dist/ui/components/OverviewSection.js +0 -107
  79. package/dist/ui/components/ScoreDisplay.d.ts +0 -8
  80. package/dist/ui/components/ScoreDisplay.js +0 -41
  81. package/dist/ui/components/TechnicalSection.d.ts +0 -7
  82. package/dist/ui/components/TechnicalSection.js +0 -110
  83. package/dist/utils/comprehensive-formatter.d.ts +0 -5
  84. package/dist/utils/comprehensive-formatter.js +0 -370
  85. package/src/commands/audit-interactive.ts +0 -149
  86. package/src/commands/audit-wizard.ts +0 -137
  87. package/src/commands/audit.ts +0 -1012
  88. package/src/commands/crawl.ts +0 -307
  89. package/src/commands/report.ts +0 -321
  90. package/src/index.ts +0 -22
  91. package/src/ui/AuditReportUI.tsx +0 -151
  92. package/src/ui/SetupWizard.tsx +0 -294
  93. package/src/ui/components/AIUnderstandingSection.tsx +0 -183
  94. package/src/ui/components/HallucinationSection.tsx +0 -172
  95. package/src/ui/components/IssuesSection.tsx +0 -140
  96. package/src/ui/components/MessageAlignmentSection.tsx +0 -203
  97. package/src/ui/components/OverviewSection.tsx +0 -157
  98. package/src/ui/components/ScoreDisplay.tsx +0 -58
  99. package/src/ui/components/TechnicalSection.tsx +0 -200
  100. package/src/utils/comprehensive-formatter.ts +0 -455
  101. package/test.sh +0 -31
  102. package/tsconfig.json +0 -25
@@ -1,940 +0,0 @@
1
- import chalk from 'chalk';
2
- import ora from 'ora';
3
- import { analyzeUrlWithRules } from 'scanner';
4
- import { calculateAIReadiness, formatAIReadinessReport } from 'scanner';
5
- import { exportAuditReport, generateScoringSummary } from 'scanner';
6
- import { writeFile, mkdir } from 'fs/promises';
7
- import { join, resolve } from 'path';
8
- import { existsSync } from 'fs';
9
- import html_to_pdf from 'html-pdf-node';
10
- import { formatComprehensiveReport, formatDetailedIssues } from '../utils/comprehensive-formatter.js';
11
- import { render } from 'ink';
12
- import React from 'react';
13
- import { AuditReportUI } from '../ui/AuditReportUI.js';
14
- import { SetupWizard } from '../ui/SetupWizard.js';
15
- export function auditCommand(program) {
16
- program
17
- .command('audit')
18
- .description('Audit a website for AI readiness')
19
- .argument('<url>', 'URL to audit')
20
- .option('-o, --output <format>', 'Output format: json, html, pdf, lhr, csv, interactive', 'interactive')
21
- .option('-r, --rules <preset>', 'Rule preset: default, strict, minimal', 'default')
22
- .option('-d, --depth <number>', 'Crawl depth (for multi-page audits)', parseInt, 1)
23
- .option('-p, --pages <urls>', 'Comma-separated list of specific pages to audit')
24
- .option('--cache-ttl <seconds>', 'Cache TTL in seconds to avoid re-fetching', parseInt)
25
- .option('--threshold <score>', 'Minimum score threshold (exit 1 if below)', parseInt)
26
- .option('--max-chunk-tokens <number>', 'Maximum tokens per content chunk', parseInt, 1200)
27
- .option('--chunking-strategy <strategy>', 'Chunking strategy: auto, heading-based, paragraph-based', 'auto')
28
- .option('--enable-chunking', 'Enable detailed content chunking analysis', false)
29
- .option('--enable-extractability', 'Enable extractability mapping', false)
30
- .option('--enable-hallucination', 'Enable hallucination detection', false)
31
- .option('--enable-llm', 'Enable LLM comprehension analysis', false)
32
- .option('--min-impact <number>', 'Minimum impact score to include', parseInt, 8)
33
- .option('--min-confidence <number>', 'Minimum confidence to include (0-1)', parseFloat, 0.7)
34
- .option('--max-issues <number>', 'Maximum issues to return', parseInt, 20)
35
- .option('--llm-provider <provider>', 'LLM provider: openai, anthropic, ollama, local')
36
- .option('--llm-model <model>', 'LLM model name')
37
- .option('--llm-base-url <url>', 'LLM API base URL')
38
- .option('--llm-api-key <key>', 'LLM API key')
39
- .action(async (url, options) => {
40
- // Detect if user wants wizard (no feature flags provided)
41
- const hasFeatureFlags = options.enableChunking ||
42
- options.enableExtractability ||
43
- options.enableHallucination ||
44
- options.enableLlm ||
45
- options.llmProvider;
46
- // If interactive mode and no feature flags, show wizard
47
- if (options.output === 'interactive' && !hasFeatureFlags) {
48
- const originalConsoleError = console.error;
49
- const originalConsoleWarn = console.warn;
50
- console.error = () => { };
51
- console.warn = () => { };
52
- let auditConfig = null;
53
- // Show setup wizard
54
- const wizardRender = render(React.createElement(SetupWizard, {
55
- initialUrl: url,
56
- onComplete: (config) => {
57
- auditConfig = config;
58
- },
59
- }));
60
- // Wait for wizard to complete
61
- await wizardRender.waitUntilExit();
62
- if (!auditConfig) {
63
- console.error = originalConsoleError;
64
- console.warn = originalConsoleWarn;
65
- console.log('\nAudit cancelled.');
66
- process.exit(0);
67
- }
68
- // Continue with audit using wizard config
69
- url = auditConfig.url;
70
- options.enableChunking = auditConfig.enableChunking;
71
- options.enableExtractability = auditConfig.enableExtractability;
72
- options.enableHallucination = auditConfig.enableHallucination;
73
- options.enableLlm = auditConfig.enableLlm;
74
- options.llmProvider = auditConfig.llmProvider;
75
- options.llmModel = auditConfig.llmModel;
76
- options.llmApiKey = auditConfig.llmApiKey;
77
- options.llmBaseUrl = auditConfig.llmBaseUrl;
78
- }
79
- // Check if interactive mode
80
- if (options.output === 'interactive') {
81
- // Suppress console.error and console.warn in interactive mode for cleaner UI
82
- const originalConsoleError = console.error;
83
- const originalConsoleWarn = console.warn;
84
- console.error = () => { }; // Suppress all console.error calls
85
- console.warn = () => { }; // Suppress all console.warn calls
86
- // Show loading UI
87
- const { waitUntilExit, clear, rerender } = render(React.createElement(AuditReportUI, {
88
- url,
89
- result: {},
90
- aiReadiness: {},
91
- loading: true,
92
- currentStep: 'Starting audit...',
93
- }));
94
- try {
95
- // Validate URL
96
- const urlObj = new URL(url);
97
- // Build scan options
98
- const scanOptions = {
99
- maxChunkTokens: options.maxChunkTokens,
100
- chunkingStrategy: options.chunkingStrategy,
101
- enableChunking: options.enableChunking,
102
- enableExtractability: options.enableExtractability,
103
- enableHallucinationDetection: options.enableHallucination,
104
- enableLLM: options.enableLlm,
105
- minImpactScore: options.minImpact,
106
- minConfidence: options.minConfidence,
107
- maxIssues: options.maxIssues,
108
- };
109
- // Configure LLM if enabled
110
- if (options.enableLlm && options.llmProvider) {
111
- scanOptions.llmConfig = {
112
- provider: options.llmProvider,
113
- model: options.llmModel,
114
- baseUrl: options.llmBaseUrl,
115
- apiKey: options.llmApiKey,
116
- };
117
- }
118
- // Update loading step
119
- rerender(React.createElement(AuditReportUI, {
120
- url: urlObj.href,
121
- result: {},
122
- aiReadiness: {},
123
- loading: true,
124
- currentStep: 'Scanning page...',
125
- }));
126
- // Run the audit
127
- const result = await analyzeUrlWithRules(url, scanOptions);
128
- // Update loading step
129
- rerender(React.createElement(AuditReportUI, {
130
- url: urlObj.href,
131
- result,
132
- aiReadiness: {},
133
- loading: true,
134
- currentStep: 'Calculating AI readiness scores...',
135
- }));
136
- // Calculate AI readiness
137
- const aiReadiness = calculateAIReadiness(result);
138
- // Clear loading and show results
139
- clear();
140
- const finalRender = render(React.createElement(AuditReportUI, {
141
- url: urlObj.href,
142
- result,
143
- aiReadiness,
144
- loading: false,
145
- }));
146
- // Wait for user to exit
147
- await finalRender.waitUntilExit();
148
- // Restore console methods
149
- console.error = originalConsoleError;
150
- console.warn = originalConsoleWarn;
151
- // Check threshold
152
- if (options.threshold !== undefined) {
153
- const overallScore = aiReadiness.overall;
154
- if (overallScore !== undefined && overallScore < options.threshold) {
155
- process.exit(1);
156
- }
157
- }
158
- }
159
- catch (error) {
160
- // Restore console methods before showing error
161
- console.error = originalConsoleError;
162
- console.warn = originalConsoleWarn;
163
- clear();
164
- // Show error in UI format instead of console.error
165
- console.log('\n' + chalk.bold.red('โŒ Audit Failed'));
166
- console.log(chalk.red('โ”€'.repeat(70)));
167
- console.log(chalk.red(error instanceof Error ? error.message : String(error)));
168
- console.log('\n' + chalk.dim('Please check the URL and your configuration.'));
169
- process.exit(1);
170
- }
171
- return;
172
- }
173
- // Non-interactive mode - original logic
174
- const spinner = ora('Starting audit...').start();
175
- try {
176
- // Validate URL
177
- const urlObj = new URL(url);
178
- spinner.text = `Auditing ${chalk.cyan(urlObj.href)}...`;
179
- // Build scan options
180
- const scanOptions = {
181
- maxChunkTokens: options.maxChunkTokens,
182
- chunkingStrategy: options.chunkingStrategy,
183
- enableChunking: options.enableChunking,
184
- enableExtractability: options.enableExtractability,
185
- enableHallucinationDetection: options.enableHallucination,
186
- enableLLM: options.enableLlm,
187
- minImpactScore: options.minImpact,
188
- minConfidence: options.minConfidence,
189
- maxIssues: options.maxIssues,
190
- };
191
- // Configure LLM if enabled
192
- if (options.enableLlm && options.llmProvider) {
193
- scanOptions.llmConfig = {
194
- provider: options.llmProvider,
195
- model: options.llmModel,
196
- baseUrl: options.llmBaseUrl,
197
- apiKey: options.llmApiKey,
198
- };
199
- }
200
- // Run the audit
201
- spinner.text = 'Scanning page...';
202
- const result = await analyzeUrlWithRules(url, scanOptions);
203
- spinner.text = 'Calculating scores...';
204
- const aiReadiness = calculateAIReadiness(result);
205
- // Format the report
206
- spinner.text = 'Generating report...';
207
- const auditReportJson = exportAuditReport(result);
208
- const auditReport = JSON.parse(auditReportJson);
209
- // Save results
210
- const outputDir = resolve(process.cwd(), '.ai-lighthouse');
211
- if (!existsSync(outputDir)) {
212
- await mkdir(outputDir, { recursive: true });
213
- }
214
- const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
215
- const baseFilename = `audit_${new URL(url).hostname}_${timestamp}`;
216
- // Handle different output formats
217
- if (options.output === 'json') {
218
- const jsonPath = join(outputDir, `${baseFilename}.json`);
219
- await writeFile(jsonPath, auditReportJson);
220
- spinner.succeed(chalk.green('Audit complete!'));
221
- console.log(chalk.dim(`Report saved to: ${jsonPath}`));
222
- }
223
- else if (options.output === 'html') {
224
- const htmlPath = join(outputDir, `${baseFilename}.html`);
225
- const html = generateHTMLReport(auditReport, aiReadiness, result);
226
- await writeFile(htmlPath, html);
227
- spinner.succeed(chalk.green('Audit complete!'));
228
- console.log(chalk.dim(`HTML report saved to: ${htmlPath}`));
229
- console.log(chalk.dim(`๐Ÿ’ก Tip: Open the HTML file and use your browser's "Print > Save as PDF" to export as PDF`));
230
- }
231
- else if (options.output === 'pdf') {
232
- spinner.text = 'Generating PDF...';
233
- const pdfPath = join(outputDir, `${baseFilename}.pdf`);
234
- const html = generateHTMLReport(auditReport, aiReadiness, result);
235
- const file = { content: html };
236
- const pdfOptions = {
237
- format: 'A4',
238
- printBackground: false,
239
- };
240
- const pdfBuffer = await html_to_pdf.generatePdf(file, pdfOptions);
241
- await writeFile(pdfPath, pdfBuffer);
242
- spinner.succeed(chalk.green('PDF generated!'));
243
- console.log(chalk.dim(`PDF report saved to: ${pdfPath}`));
244
- }
245
- else if (options.output === 'lhr') {
246
- const lhrPath = join(outputDir, `${baseFilename}.lhr.json`);
247
- const lhr = convertToLighthouseFormat(auditReport);
248
- await writeFile(lhrPath, JSON.stringify(lhr, null, 2));
249
- spinner.succeed(chalk.green('Audit complete!'));
250
- console.log(chalk.dim(`Lighthouse-compatible report saved to: ${lhrPath}`));
251
- }
252
- else if (options.output === 'csv') {
253
- const csvPath = join(outputDir, `${baseFilename}.csv`);
254
- const csv = generateCSVReport(auditReport);
255
- await writeFile(csvPath, csv);
256
- spinner.succeed(chalk.green('Audit complete!'));
257
- console.log(chalk.dim(`CSV report saved to: ${csvPath}`));
258
- }
259
- // Display summary
260
- console.log('\n' + chalk.bold('๐Ÿ“Š AI Readiness Summary'));
261
- console.log(formatAIReadinessReport(aiReadiness));
262
- console.log('\n' + chalk.bold('๐Ÿ“ˆ Technical Scores'));
263
- console.log(generateScoringSummary(result.scoring));
264
- // Display comprehensive report with all website data
265
- console.log('\n' + chalk.bold('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
266
- console.log(chalk.bold.cyan(' COMPREHENSIVE ANALYSIS '));
267
- console.log(chalk.bold('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
268
- console.log(formatComprehensiveReport(result, aiReadiness));
269
- // Display all issues
270
- if (result.issues && result.issues.length > 0) {
271
- console.log('\n' + formatDetailedIssues(result.issues));
272
- }
273
- // Check threshold
274
- if (options.threshold !== undefined) {
275
- const overallScore = auditReport?.scores?.overall;
276
- if (overallScore !== undefined && overallScore < options.threshold) {
277
- console.log(chalk.red(`\nโŒ Score ${overallScore} is below threshold ${options.threshold}`));
278
- process.exit(1);
279
- }
280
- else if (overallScore !== undefined) {
281
- console.log(chalk.green(`\nโœ… Score ${overallScore} meets threshold ${options.threshold}`));
282
- }
283
- }
284
- }
285
- catch (error) {
286
- spinner.fail(chalk.red('Audit failed'));
287
- if (error instanceof Error) {
288
- console.error(chalk.red(error.message));
289
- }
290
- process.exit(1);
291
- }
292
- });
293
- }
294
- function generateHTMLReport(report, aiReadiness, scanResult) {
295
- return `<!DOCTYPE html>
296
- <html lang="en">
297
- <head>
298
- <meta charset="UTF-8">
299
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
300
- <title>AI Lighthouse Report - ${report.input?.requested_url}</title>
301
- <style>
302
- * { margin: 0; padding: 0; box-sizing: border-box; }
303
- body {
304
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
305
- line-height: 1.6;
306
- color: #333;
307
- background: #f5f5f5;
308
- padding: 20px;
309
- }
310
- .container { color: #333; max-width: 1200px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; }
311
- h1 { color: #2563eb; margin-bottom: 10px; font-size: 2em; }
312
- h2 { color: #1e40af; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #dbeafe; padding-bottom: 10px; }
313
- h3 { color: #1e40af; margin-top: 20px; margin-bottom: 10px; font-size: 1.2em; }
314
- .header { margin-bottom: 30px; }
315
- .url { color: #64748b; font-size: 0.95em; }
316
- .timestamp { color: #94a3b8; font-size: 0.85em; }
317
-
318
- .ai-readiness-banner {
319
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
320
- color: white;
321
- padding: 30px;
322
- border-radius: 8px;
323
- margin: 20px 0;
324
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
325
- }
326
- .ai-readiness-banner h2 { color: white; border: none; margin: 0 0 20px 0; }
327
- .overall-score { font-size: 3em; font-weight: bold; margin: 10px 0; }
328
- .grade-badge { background: rgba(255,255,255,0.2); padding: 8px 16px; border-radius: 20px; display: inline-block; margin: 10px 0; }
329
- .agent-perspective { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 8px; margin: 20px 0; }
330
- .agent-status { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 15px 0; }
331
- .agent-status-item { display: flex; align-items: center; gap: 10px; }
332
-
333
- .scores { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
334
- .score-card {
335
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
336
- color: white;
337
- padding: 20px;
338
- border-radius: 8px;
339
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
340
- }
341
- .score-card h3 { font-size: 0.9em; opacity: 0.9; margin-bottom: 10px; }
342
- .score-value { font-size: 2.5em; font-weight: bold; }
343
- .grade { font-size: 1.2em; opacity: 0.8; margin-left: 10px; }
344
-
345
- .dimensions { display: grid; gap: 15px; margin: 20px 0; }
346
- .dimension { background: #f8fafc; border-left: 4px solid #cbd5e1; padding: 20px; border-radius: 4px; }
347
- .dimension.excellent { border-left-color: #10b981; }
348
- .dimension.good { border-left-color: #84cc16; }
349
- .dimension.needs-work { border-left-color: #f59e0b; }
350
- .dimension.critical { border-left-color: #dc2626; }
351
- .dimension-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
352
- .dimension-name { font-weight: 600; font-size: 1.1em; color: #1e293b; }
353
- .dimension-score { font-size: 1.5em; font-weight: bold; }
354
- .dimension-level { color: #64748b; font-size: 0.9em; margin-bottom: 10px; text-transform: uppercase; }
355
- .dimension-recommendations { margin-top: 10px; padding-left: 20px; }
356
- .dimension-recommendations li { margin: 5px 0; color: #475569; }
357
-
358
- .quick-wins { background: #fffbeb; border: 2px solid #fbbf24; padding: 20px; border-radius: 8px; margin: 20px 0; }
359
- .quick-wins h3 { color: #92400e; margin-top: 0; }
360
- .quick-win { background: white; padding: 15px; margin: 10px 0; border-radius: 4px; border-left: 3px solid #f59e0b; }
361
- .quick-win-header { font-weight: 600; color: #1e293b; margin-bottom: 8px; }
362
- .quick-win-meta { font-size: 0.85em; color: #64748b; }
363
-
364
- .priorities { margin: 20px 0; }
365
- .priority-section { background: #f8fafc; padding: 20px; border-radius: 8px; margin: 15px 0; }
366
- .priority-section.immediate { border-left: 4px solid #dc2626; }
367
- .priority-section.short-term { border-left: 4px solid #f59e0b; }
368
- .priority-section.long-term { border-left: 4px solid #3b82f6; }
369
- .priority-item { margin: 10px 0; padding-left: 20px; }
370
-
371
- .issues { margin: 20px 0; }
372
- .issue {
373
- background: #f8fafc;
374
- border-left: 4px solid #cbd5e1;
375
- padding: 15px;
376
- margin-bottom: 15px;
377
- border-radius: 4px;
378
- }
379
- .issue.critical { border-left-color: #dc2626; background: #fef2f2; }
380
- .issue.high { border-left-color: #ea580c; background: #fff7ed; }
381
- .issue.medium { border-left-color: #f59e0b; background: #fffbeb; }
382
- .issue.low { border-left-color: #84cc16; background: #f7fee7; }
383
- .issue-title { font-weight: 600; color: #1e293b; margin-bottom: 8px; }
384
- .issue-meta { font-size: 0.85em; color: #64748b; margin-bottom: 8px; }
385
- .issue-desc { color: #475569; margin-bottom: 8px; }
386
- .issue-fix { color: #0f766e; background: #f0fdfa; padding: 10px; border-radius: 4px; font-size: 0.9em; }
387
-
388
- .entity-list { display: grid; gap: 15px; }
389
- .entity { background: #f0f9ff; border: 1px solid #bae6fd; padding: 15px; border-radius: 4px; }
390
- .entity-name { font-weight: 600; color: #0c4a6e; }
391
- .entity-type { color: #0369a1; font-size: 0.85em; }
392
-
393
- .print-button {
394
- background: #2563eb;
395
- color: white;
396
- padding: 12px 24px;
397
- border: none;
398
- border-radius: 6px;
399
- cursor: pointer;
400
- font-size: 1em;
401
- margin: 20px 0;
402
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
403
- }
404
- .print-button:hover { background: #1e40af; }
405
-
406
- @media print {
407
- body { background: white; padding: 0; }
408
- .container { box-shadow: none; }
409
- .print-button { display: none; }
410
- }
411
- </style>
412
- </head>
413
- <body>
414
- <div class="container">
415
- <div class="header">
416
- <h1>๐Ÿšจ AI Lighthouse Report</h1>
417
- <div class="url">${report.input?.requested_url}</div>
418
- <div class="timestamp">Generated: ${new Date(report.scanned_at).toLocaleString()}</div>
419
- </div>
420
-
421
- ${aiReadiness ? `
422
- <div class="ai-readiness-banner">
423
- <h2>๐Ÿค– AI Readiness Assessment</h2>
424
- <div class="overall-score">${Math.round(aiReadiness.overall)}/100</div>
425
- <div class="grade-badge">Grade: ${aiReadiness.grade}</div>
426
- <div style="margin: 10px 0; opacity: 0.9;">
427
- Top ${aiReadiness.benchmark?.topPercentile || 0}% of sites โ€ข
428
- Improvement potential: +${Math.round(aiReadiness.benchmark?.improvement || 0)} points to best-in-class
429
- </div>
430
-
431
- <div class="agent-perspective">
432
- <strong>AI Agent Perspective:</strong>
433
- <div class="agent-status">
434
- <div class="agent-status-item">
435
- <span>${aiReadiness.aiPerspective?.canUnderstand ? 'โœ…' : 'โŒ'}</span>
436
- <span>Can Understand</span>
437
- </div>
438
- <div class="agent-status-item">
439
- <span>${aiReadiness.aiPerspective?.canExtract ? 'โœ…' : 'โŒ'}</span>
440
- <span>Can Extract</span>
441
- </div>
442
- <div class="agent-status-item">
443
- <span>${aiReadiness.aiPerspective?.canIndex ? 'โœ…' : 'โŒ'}</span>
444
- <span>Can Index</span>
445
- </div>
446
- <div class="agent-status-item">
447
- <span>${aiReadiness.aiPerspective?.canAnswer ? 'โœ…' : 'โŒ'}</span>
448
- <span>Can Answer Questions</span>
449
- </div>
450
- </div>
451
- <div style="margin-top: 15px;">
452
- <strong>Confidence Level:</strong> ${Math.round((aiReadiness.aiPerspective?.confidence || 0) * 100)}%
453
- </div>
454
- </div>
455
-
456
- ${(aiReadiness.aiPerspective?.mainBlockers || []).length > 0 ? `
457
- <div style="margin-top: 20px;">
458
- <strong>Main Blockers:</strong>
459
- <ul style="margin: 10px 0; padding-left: 20px;">
460
- ${(aiReadiness.aiPerspective?.mainBlockers || []).map((blocker) => `<li>${blocker}</li>`).join('')}
461
- </ul>
462
- </div>
463
- ` : ''}
464
- </div>
465
- ` : ''}
466
-
467
- <h2>๐Ÿ“Š Technical Scores</h2>
468
- <div class="scores">
469
- <div class="score-card">
470
- <h3>Overall Score</h3>
471
- <div>
472
- <span class="score-value">${report.scores?.overall || 0}</span>
473
- <span class="grade">${getLetterGrade(report.scores?.overall || 0)}</span>
474
- </div>
475
- </div>
476
- <div class="score-card">
477
- <h3>AI Readiness</h3>
478
- <div class="score-value">${report.scores?.ai_readiness || 0}</div>
479
- </div>
480
- <div class="score-card">
481
- <h3>Crawlability</h3>
482
- <div class="score-value">${report.scores?.crawlability || 0}</div>
483
- </div>
484
- <div class="score-card">
485
- <h3>Content Clarity</h3>
486
- <div class="score-value">${report.scores?.content_clarity || 0}</div>
487
- </div>
488
- <div class="score-card">
489
- <h3>Schema Coverage</h3>
490
- <div class="score-value">${report.scores?.schema_coverage || 0}</div>
491
- </div>
492
- <div class="score-card">
493
- <h3>Structure</h3>
494
- <div class="score-value">${report.scores?.structure || 0}</div>
495
- </div>
496
- </div>
497
-
498
- ${aiReadiness?.dimensions ? `
499
- <h2>๐ŸŽฏ Dimension Analysis</h2>
500
- <div class="dimensions">
501
- ${Object.entries(aiReadiness.dimensions).map(([key, dim]) => `
502
- <div class="dimension ${dim.status}">
503
- <div class="dimension-header">
504
- <div class="dimension-name">${getEmojiForDimension(key)} ${formatDimensionName(key)}</div>
505
- <div class="dimension-score">${Math.round(dim.score)}/100</div>
506
- </div>
507
- <div class="dimension-level">${dim.status}</div>
508
- ${dim.strengths && dim.strengths.length > 0 ? `
509
- <div><strong>Strengths:</strong> ${dim.strengths.join(', ')}</div>
510
- ` : ''}
511
- ${dim.weaknesses && dim.weaknesses.length > 0 ? `
512
- <div style="margin-top: 8px;"><strong>Weaknesses:</strong> ${dim.weaknesses.join(', ')}</div>
513
- ` : ''}
514
- ${dim.recommendation ? `
515
- <div class="dimension-recommendations">
516
- <div style="margin-top: 10px;">โ†’ ${dim.recommendation}</div>
517
- </div>
518
- ` : ''}
519
- </div>
520
- `).join('')}
521
- </div>
522
- ` : ''}
523
-
524
- ${aiReadiness?.quickWins && aiReadiness.quickWins.length > 0 ? `
525
- <div class="quick-wins">
526
- <h3>โšก Quick Wins (High Impact, Low Effort)</h3>
527
- ${aiReadiness.quickWins.slice(0, 5).map((win, idx) => `
528
- <div class="quick-win">
529
- <div class="quick-win-header">${idx + 1}. ${win.issue}</div>
530
- <div class="quick-win-meta">Impact: ${win.impact} | Effort: ${win.effort}</div>
531
- <div style="margin-top: 8px; color: #0f766e;">โ†’ ${win.fix}</div>
532
- </div>
533
- `).join('')}
534
- </div>
535
- ` : ''}
536
-
537
- ${aiReadiness?.roadmap ? `
538
- <h2>๐Ÿ“‹ Priority Roadmap</h2>
539
- <div class="priorities">
540
- ${aiReadiness.roadmap.immediate && aiReadiness.roadmap.immediate.length > 0 ? `
541
- <div class="priority-section immediate">
542
- <h3>๐Ÿ”ด Immediate (&lt; 1 day)</h3>
543
- ${aiReadiness.roadmap.immediate.slice(0, 5).map((item) => `
544
- <div class="priority-item">โ€ข ${item}</div>
545
- `).join('')}
546
- </div>
547
- ` : ''}
548
-
549
- ${aiReadiness.roadmap.shortTerm && aiReadiness.roadmap.shortTerm.length > 0 ? `
550
- <div class="priority-section short-term">
551
- <h3>๐ŸŸก Short-term (1-7 days)</h3>
552
- ${aiReadiness.roadmap.shortTerm.slice(0, 5).map((item) => `
553
- <div class="priority-item">โ€ข ${item}</div>
554
- `).join('')}
555
- </div>
556
- ` : ''}
557
-
558
- ${aiReadiness.roadmap.longTerm && aiReadiness.roadmap.longTerm.length > 0 ? `
559
- <div class="priority-section long-term">
560
- <h3>๐Ÿ”ต Long-term (&gt; 7 days)</h3>
561
- ${aiReadiness.roadmap.longTerm.slice(0, 5).map((item) => `
562
- <div class="priority-item">โ€ข ${item}</div>
563
- `).join('')}
564
- </div>
565
- ` : ''}
566
- </div>
567
- ` : ''}
568
-
569
- ${scanResult?.llm ? `
570
- <h2>๐Ÿ“ AI Understanding Analysis</h2>
571
- <div style="background: #f0f9ff; border: 2px solid #0ea5e9; padding: 20px; border-radius: 8px; margin: 20px 0;">
572
- <div style="margin-bottom: 15px;">
573
- <strong>Summary:</strong> ${scanResult.llm.summary || 'N/A'}
574
- </div>
575
- ${scanResult.llm.pageType ? `
576
- <div style="margin-bottom: 15px;">
577
- <strong>๐Ÿ“„ Page Type:</strong> ${scanResult.llm.pageType}
578
- </div>
579
- ` : ''}
580
- ${scanResult.llm.keyTopics && scanResult.llm.keyTopics.length > 0 ? `
581
- <div style="margin-bottom: 15px;">
582
- <strong>๐Ÿท๏ธ Key Topics:</strong> ${scanResult.llm.keyTopics.join(', ')}
583
- </div>
584
- ` : ''}
585
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
586
- ${scanResult.llm.readingLevel ? `
587
- <div><strong>๐Ÿ“Š Reading Level:</strong> ${scanResult.llm.readingLevel.description}</div>
588
- ` : ''}
589
- ${scanResult.llm.sentiment ? `
590
- <div><strong>๐ŸŽญ Sentiment:</strong> ${scanResult.llm.sentiment}</div>
591
- ` : ''}
592
- ${scanResult.llm.technicalDepth ? `
593
- <div><strong>๐ŸŽฏ Technical Depth:</strong> ${scanResult.llm.technicalDepth}</div>
594
- ` : ''}
595
- </div>
596
-
597
- ${scanResult.llm.pageTypeInsights && scanResult.llm.pageTypeInsights.length > 0 ? `
598
- <div style="margin-top: 20px; background: #dbeafe; padding: 15px; border-radius: 4px; border-left: 4px solid #0ea5e9;">
599
- <div style="font-weight: 600; margin-bottom: 10px;">๐Ÿ’ก AI-Generated Insights for ${scanResult.llm.pageType || 'This Page'}</div>
600
- <ul style="margin: 0; padding-left: 20px;">
601
- ${scanResult.llm.pageTypeInsights.map((insight) => `<li style="margin: 5px 0;">${insight}</li>`).join('')}
602
- </ul>
603
- </div>
604
- ` : ''}
605
-
606
-
607
- ${scanResult.llm.topEntities && scanResult.llm.topEntities.length > 0 ? `
608
- <div style="margin-top: 20px;">
609
- <strong>๐Ÿ” Key Entities:</strong>
610
- <div style="margin-top: 10px; display: grid; gap: 10px;">
611
- ${scanResult.llm.topEntities.slice(0, 5).map((entity) => `
612
- <div style="background: white; padding: 10px; border-radius: 4px; border-left: 3px solid #0ea5e9;">
613
- <strong>${entity.name}</strong> (${entity.type}) - ${Math.round((entity.relevance || 0) * 100)}% relevance
614
- </div>
615
- `).join('')}
616
- </div>
617
- </div>
618
- ` : ''}
619
-
620
- ${scanResult.llm.questions && scanResult.llm.questions.length > 0 ? `
621
- <div style="margin-top: 20px;">
622
- <strong>โ“ Key Questions AI Can Answer:</strong>
623
- <ol style="margin-top: 10px; padding-left: 25px;">
624
- ${scanResult.llm.questions.slice(0, 5).map((q) => `
625
- <li style="margin: 8px 0;"><span style="text-transform: uppercase; font-size: 0.8em; background: #dbeafe; padding: 2px 6px; border-radius: 3px;">${q.difficulty}</span> ${q.question}</li>
626
- `).join('')}
627
- </ol>
628
- </div>
629
- ` : ''}
630
-
631
- ${scanResult.llm.suggestedFAQ && scanResult.llm.suggestedFAQ.length > 0 ? `
632
- <div style="margin-top: 20px;">
633
- <strong>๐Ÿ’ก Suggested FAQs:</strong>
634
- <div style="margin-top: 10px; display: grid; gap: 15px;">
635
- ${scanResult.llm.suggestedFAQ.filter((f) => f.importance === 'high').slice(0, 3).map((faq) => `
636
- <div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid #f59e0b;">
637
- <div style="font-weight: 600; margin-bottom: 5px;">Q: ${faq.question}</div>
638
- <div style="color: #64748b;">A: ${faq.suggestedAnswer}</div>
639
- </div>
640
- `).join('')}
641
- </div>
642
- </div>
643
- ` : ''}
644
- </div>
645
- ` : ''}
646
-
647
- ${scanResult?.chunking ? `
648
- <h2>๐Ÿ“„ Content Chunking Analysis</h2>
649
- <div style="background: #f0fdf4; border: 2px solid #10b981; padding: 20px; border-radius: 8px; margin: 20px 0;">
650
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
651
- <div><strong>Strategy:</strong> ${scanResult.chunking.chunkingStrategy}</div>
652
- <div><strong>Total Chunks:</strong> ${scanResult.chunking.totalChunks}</div>
653
- <div><strong>Avg Tokens/Chunk:</strong> ${scanResult.chunking.averageTokensPerChunk}</div>
654
- <div><strong>Avg Noise:</strong> ${(scanResult.chunking.averageNoiseRatio * 100).toFixed(1)}%</div>
655
- </div>
656
- </div>
657
- ` : ''}
658
-
659
- ${scanResult?.extractability ? `
660
- <h2>๐Ÿ”„ Extractability Analysis</h2>
661
- <div style="background: #fef3c7; border: 2px solid #f59e0b; padding: 20px; border-radius: 8px; margin: 20px 0;">
662
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 15px;">
663
- <div><strong>Overall Score:</strong> ${scanResult.extractability.score.extractabilityScore}/100</div>
664
- <div><strong>Server-Rendered:</strong> ${scanResult.extractability.score.serverRenderedPercent}%</div>
665
- </div>
666
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
667
- <div><strong>Text Extractable:</strong> ${scanResult.extractability.contentTypes.text.percentage}%</div>
668
- <div><strong>Images Extractable:</strong> ${scanResult.extractability.contentTypes.images.percentage}%</div>
669
- </div>
670
- </div>
671
- ` : ''}
672
-
673
- ${scanResult?.hallucinationReport ? `
674
- <h2>โš ๏ธ Hallucination Risk Assessment</h2>
675
- <div style="background: #fef2f2; border: 2px solid #ef4444; padding: 20px; border-radius: 8px; margin: 20px 0;">
676
- <div style="font-size: 1.5em; font-weight: bold; margin-bottom: 15px;">
677
- Risk Score: ${scanResult.hallucinationReport.hallucinationRiskScore}/100
678
- </div>
679
-
680
- ${scanResult.hallucinationReport.factCheckSummary ? `
681
- <div style="margin-top: 20px; background: white; padding: 15px; border-radius: 4px;">
682
- <strong>๐Ÿ“Š Fact Check Summary:</strong>
683
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 10px;">
684
- <div>โœ… Verified: ${scanResult.hallucinationReport.factCheckSummary.verifiedFacts}</div>
685
- <div>โ“ Unverified: ${scanResult.hallucinationReport.factCheckSummary.unverifiedFacts}</div>
686
- <div>โš ๏ธ Contradictions: ${scanResult.hallucinationReport.factCheckSummary.contradictions}</div>
687
- <div>๐Ÿค” Ambiguities: ${scanResult.hallucinationReport.factCheckSummary.ambiguities}</div>
688
- </div>
689
- </div>
690
- ` : ''}
691
-
692
- ${scanResult.hallucinationReport.factCheckSummary && scanResult.hallucinationReport.factCheckSummary.unverifiedFacts > 0 ? `
693
- <div style="margin-top: 15px; background: #fef9c3; border-left: 4px solid #eab308; padding: 15px; border-radius: 4px;">
694
- <div style="display: flex; align-items: start; gap: 10px;">
695
- <span style="font-size: 1.5em;">๐Ÿ’ก</span>
696
- <div>
697
- <div style="font-weight: 600; margin-bottom: 8px;">Why Some Facts Can't Be Verified</div>
698
- <div style="color: #854d0e; margin-bottom: 8px;">
699
- AI systems need external sources to verify claims. When information lacks citations, links, or
700
- references, it becomes difficult to confirm accuracy, increasing the risk of AI hallucination or misinformation.
701
- </div>
702
- <div style="font-weight: 600; margin-top: 12px; margin-bottom: 6px;">Best Practices:</div>
703
- <ul style="margin: 0; padding-left: 20px; color: #854d0e;">
704
- <li>Add source links directly in or near claim text</li>
705
- <li>Include dates for time-sensitive information</li>
706
- <li>Link to authoritative sources (research, official docs)</li>
707
- <li>Provide context for statistics and data points</li>
708
- <li>Use schema.org markup to specify citations</li>
709
- </ul>
710
- </div>
711
- </div>
712
- </div>
713
- ` : ''}
714
-
715
- ${scanResult.hallucinationReport.triggers && scanResult.hallucinationReport.triggers.length > 0 ? `
716
- <div style="margin-top: 20px;">
717
- <strong>๐Ÿšจ Identified Triggers:</strong>
718
- <div style="margin-top: 10px; display: grid; gap: 10px;">
719
- ${scanResult.hallucinationReport.triggers.filter((t) => t.severity === 'high' || t.severity === 'critical').slice(0, 5).map((trigger) => `
720
- <div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid #dc2626;">
721
- <div style="font-weight: 600; text-transform: uppercase; font-size: 0.85em; color: #dc2626;">${trigger.type} - ${trigger.severity}</div>
722
- <div style="margin-top: 5px;">${trigger.description}</div>
723
- <div style="margin-top: 5px; font-size: 0.9em; color: #64748b;">Confidence: ${Math.round((trigger.confidence || 0) * 100)}%</div>
724
- </div>
725
- `).join('')}
726
- </div>
727
- </div>
728
- ` : ''}
729
-
730
- ${scanResult.hallucinationReport.recommendations && scanResult.hallucinationReport.recommendations.length > 0 ? `
731
- <div style="margin-top: 20px;">
732
- <strong>๐Ÿ’ก Recommendations:</strong>
733
- <ul style="margin-top: 10px; padding-left: 25px;">
734
- ${scanResult.hallucinationReport.recommendations.slice(0, 3).map((rec) => `<li style="margin: 5px 0;">${rec}</li>`).join('')}
735
- </ul>
736
- </div>
737
- ` : ''}
738
- </div>
739
- ` : ''}
740
-
741
- ${scanResult?.mirrorReport ? `
742
- <h2>๐Ÿ” AI Misunderstanding Check</h2>
743
- <div style="background: #faf5ff; border: 2px solid #a855f7; padding: 20px; border-radius: 8px; margin: 20px 0;">
744
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 15px;">
745
- <div><strong>๐ŸŽฏ Alignment Score:</strong> ${scanResult.mirrorReport.summary.alignmentScore}/100</div>
746
- <div><strong>๐Ÿ“– Clarity Score:</strong> ${scanResult.mirrorReport.summary.clarityScore}/100</div>
747
- <div><strong>โš ๏ธ Critical Issues:</strong> ${scanResult.mirrorReport.summary.critical}</div>
748
- <div><strong>๐ŸŸก Major Issues:</strong> ${scanResult.mirrorReport.summary.major}</div>
749
- </div>
750
-
751
- ${scanResult.mirrorReport.llmInterpretation ? `
752
- <div style="background: #dbeafe; padding: 16px; border-radius: 8px; margin-bottom: 16px; border: 1px solid #3b82f6;">
753
- <div style="font-weight: bold; margin-bottom: 12px; color: #1e40af;">
754
- ๐Ÿค– What AI Actually Understood (${Math.round(scanResult.mirrorReport.llmInterpretation.confidence * 100)}% confident)
755
- </div>
756
-
757
- <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
758
- ${scanResult.mirrorReport.llmInterpretation.productName ? `
759
- <div>
760
- <div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">PRODUCT NAME</div>
761
- <div style="font-size: 14px;">${scanResult.mirrorReport.llmInterpretation.productName}</div>
762
- </div>
763
- ` : ''}
764
-
765
- ${scanResult.mirrorReport.llmInterpretation.purpose ? `
766
- <div style="grid-column: 1 / -1;">
767
- <div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">MAIN PURPOSE</div>
768
- <div style="font-size: 14px;">${scanResult.mirrorReport.llmInterpretation.purpose}</div>
769
- </div>
770
- ` : ''}
771
-
772
- ${scanResult.mirrorReport.llmInterpretation.valueProposition ? `
773
- <div style="grid-column: 1 / -1;">
774
- <div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">๐Ÿ’Ž UNIQUE VALUE</div>
775
- <div style="font-size: 14px; font-weight: 600; color: #7c3aed;">${scanResult.mirrorReport.llmInterpretation.valueProposition}</div>
776
- </div>
777
- ` : ''}
778
-
779
- ${scanResult.mirrorReport.llmInterpretation.keyBenefits && scanResult.mirrorReport.llmInterpretation.keyBenefits.length > 0 ? `
780
- <div>
781
- <div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">KEY BENEFITS</div>
782
- <ul style="font-size: 14px; margin: 0; padding-left: 20px;">
783
- ${scanResult.mirrorReport.llmInterpretation.keyBenefits.map((b) => `<li style="margin: 4px 0;">${b}</li>`).join('')}
784
- </ul>
785
- </div>
786
- ` : ''}
787
-
788
- ${scanResult.mirrorReport.llmInterpretation.keyFeatures && scanResult.mirrorReport.llmInterpretation.keyFeatures.length > 0 ? `
789
- <div>
790
- <div style="font-size: 11px; font-weight: 600; color: #64748b; margin-bottom: 4px;">KEY FEATURES</div>
791
- <ul style="font-size: 14px; margin: 0; padding-left: 20px;">
792
- ${scanResult.mirrorReport.llmInterpretation.keyFeatures.slice(0, 3).map((f) => `<li style="margin: 4px 0;">${f}</li>`).join('')}
793
- </ul>
794
- </div>
795
- ` : ''}
796
- </div>
797
- </div>
798
- ` : ''}
799
-
800
- ${scanResult.mirrorReport.mismatches && scanResult.mirrorReport.mismatches.length > 0 ? `
801
- <div style="margin-top: 20px;">
802
- <strong>Priority Mismatches:</strong>
803
- <div style="margin-top: 10px; display: grid; gap: 10px;">
804
- ${scanResult.mirrorReport.mismatches.filter((m) => m.severity === 'critical' || m.severity === 'major').slice(0, 5).map((mismatch) => `
805
- <div style="background: white; padding: 15px; border-radius: 4px; border-left: 3px solid ${mismatch.severity === 'critical' ? '#dc2626' : '#f59e0b'};">
806
- <div style="font-weight: 600;">${mismatch.severity === 'critical' ? '๐Ÿ”ด' : '๐ŸŸก'} ${mismatch.field}</div>
807
- <div style="margin-top: 5px; color: #64748b;">${mismatch.description}</div>
808
- <div style="margin-top: 10px; padding: 10px; background: #f0fdfa; border-radius: 4px;">
809
- <strong>Fix:</strong> ${mismatch.recommendation}
810
- </div>
811
- </div>
812
- `).join('')}
813
- </div>
814
- </div>
815
- ` : ''}
816
-
817
- ${scanResult.mirrorReport.recommendations && scanResult.mirrorReport.recommendations.length > 0 ? `
818
- <div style="margin-top: 20px;">
819
- <strong>๐Ÿ’ก Top Recommendations:</strong>
820
- <ul style="margin-top: 10px; padding-left: 25px;">
821
- ${scanResult.mirrorReport.recommendations.slice(0, 3).map((rec) => `<li style="margin: 5px 0;">${rec}</li>`).join('')}
822
- </ul>
823
- </div>
824
- ` : ''}
825
- </div>
826
- ` : ''}
827
-
828
- <h2>โš ๏ธ All Issues (${report.issues?.length || 0})</h2>
829
- <div class="issues">
830
- ${(report.issues || []).map((issue) => `
831
- <div class="issue ${issue.severity}">
832
- <div class="issue-title">${issue.message}</div>
833
- <div class="issue-meta">
834
- <span style="text-transform: uppercase; font-weight: 600;">${issue.severity}</span>
835
- ยท Impact: ${issue.impact}
836
- ยท Category: ${issue.category}
837
- </div>
838
- <div class="issue-desc">${issue.evidence || 'No additional evidence'}</div>
839
- <div class="issue-fix"><strong>Fix:</strong> ${issue.suggested_fix}</div>
840
- </div>
841
- `).join('')}
842
- </div>
843
-
844
- ${(report.entities || []).length > 0 ? `
845
- <h2>๐Ÿท๏ธ Detected Entities (${(report.entities || []).length})</h2>
846
- <div class="entity-list">
847
- ${(report.entities || []).map((entity) => `
848
- <div class="entity">
849
- <div class="entity-name">${entity.name}</div>
850
- <div class="entity-type">${entity.type} ยท Source: ${entity.source}</div>
851
- ${entity.description ? `<div style="margin-top: 8px; color: #334155;">${entity.description}</div>` : ''}
852
- </div>
853
- `).join('')}
854
- </div>
855
- ` : ''}
856
- </div>
857
- </body>
858
- </html>`;
859
- }
860
- function getLetterGrade(score) {
861
- if (score >= 90)
862
- return 'A';
863
- if (score >= 80)
864
- return 'B';
865
- if (score >= 70)
866
- return 'C';
867
- if (score >= 60)
868
- return 'D';
869
- return 'F';
870
- }
871
- function getEmojiForDimension(name) {
872
- const emojiMap = {
873
- 'contentQuality': '๐Ÿ“',
874
- 'discoverability': '๐Ÿ”',
875
- 'extractability': '๐Ÿ”„',
876
- 'comprehensibility': '๐Ÿง ',
877
- 'trustworthiness': 'โœ…',
878
- 'structured': '๐Ÿ“Š',
879
- 'semantic': '๐Ÿท๏ธ'
880
- };
881
- return emojiMap[name] || '๐Ÿ“Œ';
882
- }
883
- function formatDimensionName(key) {
884
- const nameMap = {
885
- 'contentQuality': 'Content Quality',
886
- 'discoverability': 'Discoverability',
887
- 'extractability': 'Extractability',
888
- 'comprehensibility': 'Comprehensibility',
889
- 'trustworthiness': 'Trustworthiness'
890
- };
891
- return nameMap[key] || key;
892
- }
893
- function convertToLighthouseFormat(report) {
894
- return {
895
- lighthouseVersion: '1.0.0',
896
- userAgent: 'AI-Lighthouse/1.0.0',
897
- fetchTime: report.scanned_at,
898
- requestedUrl: report.input?.requested_url,
899
- finalUrl: report.input?.final_url,
900
- categories: {
901
- 'ai-readiness': {
902
- id: 'ai-readiness',
903
- title: 'AI Readiness',
904
- score: (report.scores?.ai_readiness || 0) / 100,
905
- },
906
- 'crawlability': {
907
- id: 'crawlability',
908
- title: 'Crawlability',
909
- score: (report.scores?.crawlability || 0) / 100,
910
- },
911
- 'content-clarity': {
912
- id: 'content-clarity',
913
- title: 'Content Clarity',
914
- score: (report.scores?.content_clarity || 0) / 100,
915
- },
916
- },
917
- audits: (report.issues || []).reduce((acc, issue, idx) => {
918
- acc[`issue-${idx}`] = {
919
- id: issue.id,
920
- title: issue.message,
921
- description: issue.evidence || '',
922
- score: issue.severity === 'critical' ? 0 : issue.severity === 'high' ? 0.25 : 0.5,
923
- displayValue: issue.suggested_fix,
924
- };
925
- return acc;
926
- }, {}),
927
- };
928
- }
929
- function generateCSVReport(report) {
930
- const headers = ['ID', 'Severity', 'Category', 'Message', 'Impact', 'Suggested Fix'];
931
- const rows = (report.issues || []).map((issue) => [
932
- issue.id,
933
- issue.severity,
934
- issue.category,
935
- `"${issue.message.replace(/"/g, '""')}"`,
936
- issue.impact,
937
- `"${issue.suggested_fix.replace(/"/g, '""')}"`,
938
- ]);
939
- return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
940
- }