File size: 4,752 Bytes
21cac8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
const AnalysisService = require('../../backend/services/analysisService');
const ScreenshotService = require('../../backend/services/screenshotService');
const AICritiqueService = require('../../backend/services/aiCritiqueService');
const VisualDesignAnalyzer = require('../../backend/services/visualDesignAnalyzer');
const CodeGenerationService = require('../../backend/services/codeGenerationService');
const { initializeDatabase } = require('../../backend/database/init');

class UXAnalyzer {
  constructor(config) {
    this.config = config;
    this.services = null;
  }

  async initialize() {
    if (this.services) return;

    // Initialize database
    await initializeDatabase();

    // Initialize services
    const screenshotService = new ScreenshotService(this.config);
    const aiCritiqueService = new AICritiqueService(this.config);
    const visualDesignAnalyzer = new VisualDesignAnalyzer();
    const codeGenerationService = new CodeGenerationService({
      geminiApiKey: this.config.ai.geminiApiKey,
      logger: console
    });

    const analysisService = new AnalysisService({
      screenshotService,
      aiCritiqueService,
      visualDesignAnalyzer,
      codeGenerationService,
      config: this.config
    });

    this.services = {
      analysisService,
      screenshotService,
      aiCritiqueService,
      visualDesignAnalyzer,
      codeGenerationService
    };
  }

  async analyze(url, options = {}, progressCallback = null) {
    await this.initialize();

    const defaultOptions = {
      viewports: ['desktop', 'tablet', 'mobile'],
      analysisType: 'comprehensive',
      includeAccessibility: false,
      includeCodeGeneration: true
    };

    const finalOptions = { ...defaultOptions, ...options };

    // Start analysis
    const analysisResult = await this.services.analysisService.analyzeWebsite(url, finalOptions);
    const analysisId = analysisResult.analysisId;

    // Poll for progress if callback provided
    if (progressCallback) {
      await this.monitorProgress(analysisId, progressCallback);
    }

    // Wait for completion and get results
    const result = await this.waitForCompletion(analysisId);

    return this.formatResult(result);
  }

  async monitorProgress(analysisId, callback) {
    const checkProgress = async () => {
      try {
        const result = await this.services.analysisService.getAnalysisResult(analysisId);

        if (result) {
          callback({
            percent: result.progress || 0,
            stage: result.stage || 'Processing',
            status: result.status
          });

          if (result.status === 'completed' || result.status === 'failed') {
            return;
          }
        }

        // Continue monitoring
        setTimeout(checkProgress, 2000);
      } catch (error) {
        // Continue monitoring on error
        setTimeout(checkProgress, 2000);
      }
    };

    await checkProgress();
  }

  async waitForCompletion(analysisId, maxWaitTime = 300000) { // 5 minutes max
    const startTime = Date.now();

    return new Promise((resolve, reject) => {
      const checkCompletion = async () => {
        try {
          const result = await this.services.analysisService.getAnalysisResult(analysisId);

          if (result && result.status === 'completed') {
            resolve(result);
            return;
          }

          if (result && result.status === 'failed') {
            reject(new Error(result.errorMessage || 'Analysis failed'));
            return;
          }

          // Check timeout
          if (Date.now() - startTime > maxWaitTime) {
            reject(new Error('Analysis timeout'));
            return;
          }

          // Continue waiting
          setTimeout(checkCompletion, 2000);
        } catch (error) {
          reject(error);
        }
      };

      checkCompletion();
    });
  }

  formatResult(rawResult) {
    const formatted = {
      id: rawResult.id,
      url: rawResult.url,
      status: rawResult.status,
      createdAt: rawResult.createdAt,
      completedAt: rawResult.completedAt,
      screenshots: rawResult.screenshots || [],
      results: rawResult.results || {}
    };

    // Extract key results for easier CLI access
    if (rawResult.results) {
      formatted.accessibility = rawResult.results.accessibility;
      formatted.uxCritique = rawResult.results.ux_critique;
      formatted.report = rawResult.results.final_report;
      formatted.implementationCode = rawResult.results.implementation_code;
    }

    return formatted;
  }

  async cleanup() {
    if (this.services?.screenshotService) {
      // Close any open browsers
      await this.services.screenshotService.cleanup?.();
    }
  }
}

module.exports = UXAnalyzer;