ginipick commited on
Commit
3453b9b
·
verified ·
1 Parent(s): 4e5cd2c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +386 -374
index.html CHANGED
@@ -3,485 +3,497 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Advanced Facial Emotion Analysis System</title>
7
- <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
8
- <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/face-detection"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
- :root {
12
- --primary: #3498db;
13
- --success: #2ecc71;
14
- --warning: #f1c40f;
15
- --danger: #e74c3c;
16
- --dark: #2c3e50;
17
- --light: #ecf0f1;
18
- }
19
-
20
  * {
21
  margin: 0;
22
  padding: 0;
23
  box-sizing: border-box;
24
- font-family: 'Segoe UI', system-ui, sans-serif;
25
  }
26
 
27
- body {
28
- background: var(--dark);
29
- color: var(--light);
30
- line-height: 1.6;
31
- }
32
-
33
- .dashboard {
34
- display: grid;
35
- grid-template-columns: 70% 30%;
36
- gap: 20px;
37
- padding: 20px;
38
- max-width: 1600px;
39
  margin: 0 auto;
 
 
 
40
  }
41
 
42
- .main-panel {
43
- background: rgba(255,255,255,0.1);
44
- border-radius: 15px;
45
  padding: 20px;
 
 
 
 
46
  }
47
 
48
- .controls-panel {
49
- background: rgba(255,255,255,0.1);
50
- border-radius: 15px;
51
- padding: 20px;
 
 
 
 
 
52
  }
53
 
54
- .video-container {
55
- position: relative;
56
- width: 100%;
57
- border-radius: 10px;
58
- overflow: hidden;
59
- background: #000;
60
  }
61
 
62
- #video, #canvas {
63
- width: 100%;
64
- transform: scaleX(-1);
 
 
65
  }
66
 
67
- #canvas {
68
- position: absolute;
69
- top: 0;
70
- left: 0;
71
  }
72
 
73
- .settings-group {
74
- margin: 15px 0;
75
- padding: 15px;
76
- background: rgba(255,255,255,0.05);
77
- border-radius: 8px;
78
  }
79
 
80
- .settings-group h3 {
81
- color: var(--primary);
82
- margin-bottom: 10px;
83
  }
84
 
85
- .control-button {
86
  padding: 10px 20px;
87
  border: none;
88
- border-radius: 5px;
89
- background: var(--primary);
90
- color: white;
91
  cursor: pointer;
92
- transition: all 0.3s ease;
93
- margin: 5px;
94
- width: 100%;
95
  }
96
 
97
- .control-button:hover {
98
- opacity: 0.9;
99
- transform: translateY(-2px);
100
  }
101
 
102
- .emotion-indicator {
103
- display: grid;
104
- grid-template-columns: repeat(2, 1fr);
105
- gap: 10px;
106
- margin-top: 10px;
107
  }
108
 
109
- .emotion-item {
110
- padding: 10px;
111
- background: rgba(255,255,255,0.05);
112
- border-radius: 5px;
113
- text-align: center;
114
- transition: all 0.3s ease;
115
  }
116
 
117
- .emotion-item.active {
118
- background: var(--success);
119
- transform: scale(1.05);
 
 
 
 
120
  }
121
 
122
- .stats-container {
123
- margin-top: 20px;
 
 
124
  }
125
 
126
- .meter {
127
- height: 20px;
128
- background: rgba(255,255,255,0.1);
129
- border-radius: 10px;
130
  overflow: hidden;
131
- margin: 10px 0;
132
  }
133
 
134
- .meter-fill {
135
- height: 100%;
136
- background: var(--primary);
137
- transition: width 0.3s ease;
 
138
  }
139
 
140
- .privacy-toggle {
141
- margin: 10px 0;
 
 
 
 
142
  }
143
 
144
- #emotionChart {
145
- margin-top: 20px;
146
- background: rgba(255,255,255,0.05);
147
- border-radius: 8px;
148
- padding: 10px;
149
  }
150
 
151
- .fps-counter {
152
- position: absolute;
153
- top: 10px;
154
- right: 10px;
155
- background: rgba(0,0,0,0.7);
156
- padding: 5px 10px;
157
- border-radius: 5px;
158
- font-size: 14px;
159
  }
160
 
161
- .detection-settings {
162
- display: grid;
163
- gap: 10px;
164
- margin: 10px 0;
 
 
165
  }
166
 
167
- .slider-control {
168
  display: flex;
169
  flex-direction: column;
170
- gap: 5px;
171
  }
172
 
173
- input[type="range"] {
174
- width: 100%;
175
- background: var(--primary);
176
  }
177
 
178
- .face-count {
179
- font-size: 1.2em;
180
- text-align: center;
181
- margin: 10px 0;
182
- color: var(--success);
 
183
  }
184
 
185
- @keyframes pulse {
186
- 0% { transform: scale(1); }
187
- 50% { transform: scale(1.05); }
188
- 100% { transform: scale(1); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  }
190
 
191
- .detecting {
192
- animation: pulse 2s infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
  </style>
195
  </head>
196
  <body>
197
- <div class="dashboard">
198
- <div class="main-panel">
199
- <div class="video-container">
200
- <video id="video" playsinline></video>
201
- <canvas id="canvas"></canvas>
202
- <div class="fps-counter">FPS: <span id="fpsCounter">0</span></div>
203
- </div>
204
- <canvas id="emotionChart"></canvas>
205
  </div>
206
 
207
- <div class="controls-panel">
208
- <div class="settings-group">
209
- <h3>Detection Controls</h3>
210
- <button id="toggleDetection" class="control-button">Start Detection</button>
211
- <button id="togglePrivacy" class="control-button">Toggle Face Blur</button>
212
-
213
- <div class="detection-settings">
214
- <div class="slider-control">
215
- <label>Detection Sensitivity</label>
216
- <input type="range" id="sensitivity" min="0" max="100" value="50">
217
- </div>
218
- <div class="slider-control">
219
- <label>Minimum Face Size</label>
220
- <input type="range" id="minFaceSize" min="20" max="200" value="50">
221
- </div>
222
- </div>
223
- </div>
224
-
225
- <div class="settings-group">
226
- <h3>Current Analysis</h3>
227
- <div class="face-count">Detected Faces: <span id="faceCount">0</span></div>
228
- <div class="emotion-indicator">
229
- <div class="emotion-item" data-emotion="happy">😊 Happy</div>
230
- <div class="emotion-item" data-emotion="sad">😢 Sad</div>
231
- <div class="emotion-item" data-emotion="angry">😠 Angry</div>
232
- <div class="emotion-item" data-emotion="neutral">😐 Neutral</div>
233
- <div class="emotion-item" data-emotion="surprised">😲 Surprised</div>
234
- <div class="emotion-item" data-emotion="fearful">😨 Fearful</div>
235
- </div>
236
  </div>
237
-
238
- <div class="settings-group">
239
- <h3>Confidence Level</h3>
240
- <div class="meter">
241
- <div id="confidenceMeter" class="meter-fill" style="width: 0%"></div>
242
- </div>
243
  </div>
244
  </div>
245
- </div>
246
 
247
- <script>
248
- let model;
249
- let isDetecting = false;
250
- let blurFaces = false;
251
- let lastFrameTime = 0;
252
- let smoothedEmotions = {};
253
-
254
- const emotions = ['happy', 'sad', 'angry', 'neutral', 'surprised', 'fearful'];
255
- emotions.forEach(emotion => {
256
- smoothedEmotions[emotion] = 0;
257
- });
258
-
259
- async function initializeSystem() {
260
- try {
261
- // Initialize face detection model
262
- model = await faceDetection.createDetector(
263
- faceDetection.SupportedModels.MediaPipeFaceDetector,
264
- {
265
- runtime: 'tfjs',
266
- refineLandmarks: true,
267
- maxFaces: 10
268
- }
269
- );
270
-
271
- // Setup video
272
- const video = document.getElementById('video');
273
- const stream = await navigator.mediaDevices.getUserMedia({
274
- video: {
275
- width: 640,
276
- height: 480,
277
- facingMode: 'user'
278
- }
279
- });
280
- video.srcObject = stream;
281
- await video.play();
282
-
283
- // Setup canvas
284
- const canvas = document.getElementById('canvas');
285
- canvas.width = video.videoWidth;
286
- canvas.height = video.videoHeight;
287
 
288
- // Initialize emotion chart
289
- initializeEmotionChart();
 
290
 
291
- // Add event listeners
292
- setupEventListeners();
 
 
 
293
 
294
- } catch (error) {
295
- console.error('Initialization error:', error);
296
- alert('Failed to initialize face detection system');
 
 
 
 
 
 
 
 
 
297
  }
298
- }
299
 
300
- function setupEventListeners() {
301
- document.getElementById('toggleDetection').addEventListener('click', toggleDetection);
302
- document.getElementById('togglePrivacy').addEventListener('click', () => blurFaces = !blurFaces);
303
- document.getElementById('sensitivity').addEventListener('input', updateDetectionSettings);
304
- document.getElementById('minFaceSize').addEventListener('input', updateDetectionSettings);
305
- }
 
 
 
306
 
307
- async function detectFaces() {
308
- if (!isDetecting) return;
 
 
 
 
 
 
 
 
 
309
 
310
- const video = document.getElementById('video');
311
- const canvas = document.getElementById('canvas');
312
- const ctx = canvas.getContext('2d');
 
313
 
314
- // Calculate FPS
315
- const now = performance.now();
316
- const fps = 1000 / (now - lastFrameTime);
317
- lastFrameTime = now;
318
- document.getElementById('fpsCounter').textContent = Math.round(fps);
 
 
 
 
 
 
319
 
320
- try {
321
- const faces = await model.estimateFaces(video, {
322
- flipHorizontal: false
323
- });
 
324
 
325
- ctx.clearRect(0, 0, canvas.width, canvas.height);
 
 
 
326
 
327
- faces.forEach((face, index) => {
328
- drawFaceBox(ctx, face);
329
- if (blurFaces) {
330
- blurFaceRegion(ctx, face);
331
- }
332
- analyzeEmotion(face);
333
- });
334
 
335
- document.getElementById('faceCount').textContent = faces.length;
336
- updateConfidenceMeter(faces);
337
- updateEmotionChart();
 
338
 
339
- } catch (error) {
340
- console.error('Detection error:', error);
 
 
 
 
 
 
 
 
341
  }
342
 
343
- requestAnimationFrame(detectFaces);
344
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
- function drawFaceBox(ctx, face) {
347
- const box = face.box;
348
- ctx.strokeStyle = '#3498db';
349
- ctx.lineWidth = 2;
350
- ctx.strokeRect(box.xMin, box.yMin, box.width, box.height);
351
-
352
- // Draw landmarks
353
- if (face.keypoints) {
354
- ctx.fillStyle = '#2ecc71';
355
- face.keypoints.forEach(keypoint => {
356
- ctx.beginPath();
357
- ctx.arc(keypoint.x, keypoint.y, 2, 0, 2 * Math.PI);
358
- ctx.fill();
359
- });
360
  }
361
- }
362
 
363
- function blurFaceRegion(ctx, face) {
364
- const box = face.box;
365
- ctx.filter = 'blur(10px)';
366
- ctx.fillStyle = 'rgba(0,0,0,0.5)';
367
- ctx.fillRect(box.xMin, box.yMin, box.width, box.height);
368
- ctx.filter = 'none';
369
- }
370
-
371
- function analyzeEmotion(face) {
372
- // Simplified emotion analysis based on facial landmarks
373
- const keypoints = face.keypoints;
374
- if (!keypoints) return;
375
-
376
- // Calculate emotion probabilities (simplified)
377
- const emotions = {
378
- happy: Math.random(),
379
- sad: Math.random(),
380
- angry: Math.random(),
381
- neutral: Math.random(),
382
- surprised: Math.random(),
383
- fearful: Math.random()
384
- };
385
-
386
- // Apply smoothing
387
- Object.keys(emotions).forEach(emotion => {
388
- smoothedEmotions[emotion] = smoothedEmotions[emotion] * 0.8 + emotions[emotion] * 0.2;
389
- });
390
-
391
- // Update UI
392
- updateEmotionIndicators(smoothedEmotions);
393
- }
394
 
395
- function updateEmotionIndicators(emotions) {
396
- const maxEmotion = Object.entries(emotions).reduce((a, b) => a[1] > b[1] ? a : b)[0];
397
- document.querySelectorAll('.emotion-item').forEach(item => {
398
- item.classList.remove('active');
399
- if (item.dataset.emotion === maxEmotion) {
400
- item.classList.add('active');
 
 
 
 
401
  }
402
- });
403
- }
404
 
405
- function updateConfidenceMeter(faces) {
406
- if (faces.length === 0) return;
407
- const avgConfidence = faces.reduce((sum, face) => sum + face.box.score, 0) / faces.length;
408
- document.getElementById('confidenceMeter').style.width = `${avgConfidence * 100}%`;
409
- }
410
-
411
- function toggleDetection() {
412
- isDetecting = !isDetecting;
413
- const button = document.getElementById('toggleDetection');
414
- button.textContent = isDetecting ? 'Stop Detection' : 'Start Detection';
415
- button.style.background = isDetecting ? '#e74c3c' : '#3498db';
416
-
417
- if (isDetecting) {
418
- detectFaces();
419
  }
420
- }
421
-
422
- function updateDetectionSettings() {
423
- // Implementation for detection sensitivity and minimum face size
424
- const sensitivity = document.getElementById('sensitivity').value;
425
- const minFaceSize = document.getElementById('minFaceSize').value;
426
- // Update model parameters here
427
- }
428
 
429
- let emotionChart;
430
- function initializeEmotionChart() {
431
- const ctx = document.getElementById('emotionChart').getContext('2d');
432
- emotionChart = new Chart(ctx, {
433
- type: 'line',
434
- data: {
435
- labels: [],
436
- datasets: emotions.map(emotion => ({
437
- label: emotion,
438
- data: [],
439
- borderColor: getEmotionColor(emotion),
440
- fill: false
441
- }))
442
- },
443
- options: {
444
- responsive: true,
445
- scales: {
446
- y: {
447
- beginAtZero: true,
448
- max: 1
 
 
 
 
 
449
  }
 
 
450
  }
451
  }
452
- });
453
- }
454
 
455
- function updateEmotionChart() {
456
- const timestamp = new Date().toLocaleTimeString();
457
-
458
- emotionChart.data.labels.push(timestamp);
459
- emotions.forEach((emotion, index) => {
460
- emotionChart.data.datasets[index].data.push(smoothedEmotions[emotion]);
461
- });
462
-
463
- if (emotionChart.data.labels.length > 30) {
464
- emotionChart.data.labels.shift();
465
- emotionChart.data.datasets.forEach(dataset => dataset.data.shift());
466
  }
467
 
468
- emotionChart.update();
469
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
- function getEmotionColor(emotion) {
472
- const colors = {
473
- happy: '#2ecc71',
474
- sad: '#3498db',
475
- angry: '#e74c3c',
476
- neutral: '#95a5a6',
477
- surprised: '#f1c40f',
478
- fearful: '#9b59b6'
479
- };
480
- return colors[emotion] || '#000000';
 
481
  }
482
 
483
- // Initialize the system when page loads
484
- window.onload = initializeSystem;
 
485
  </script>
486
  </body>
487
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Screen Monitor Pro</title>
7
+ <script src="https://unpkg.com/tesseract.js@v2.1.0/dist/tesseract.min.js"></script>
 
 
8
  <style>
 
 
 
 
 
 
 
 
 
9
  * {
10
  margin: 0;
11
  padding: 0;
12
  box-sizing: border-box;
13
+ font-family: -apple-system, system-ui, sans-serif;
14
  }
15
 
16
+ .app-container {
17
+ max-width: 1200px;
 
 
 
 
 
 
 
 
 
 
18
  margin: 0 auto;
19
+ padding: 20px;
20
+ background: #f8fafc;
21
+ min-height: 100vh;
22
  }
23
 
24
+ .header {
25
+ background: white;
 
26
  padding: 20px;
27
+ border-radius: 8px;
28
+ margin-bottom: 20px;
29
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
30
+ text-align: center;
31
  }
32
 
33
+ .status-bar {
34
+ display: flex;
35
+ justify-content: space-between;
36
+ align-items: center;
37
+ background: white;
38
+ padding: 15px;
39
+ border-radius: 8px;
40
+ margin-bottom: 20px;
41
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
42
  }
43
 
44
+ .status-indicator {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 8px;
 
 
48
  }
49
 
50
+ .status-dot {
51
+ width: 10px;
52
+ height: 10px;
53
+ border-radius: 50%;
54
+ background: #cbd5e1;
55
  }
56
 
57
+ .status-dot.active {
58
+ background: #22c55e;
59
+ animation: pulse 1s infinite;
 
60
  }
61
 
62
+ @keyframes pulse {
63
+ 0% { opacity: 1; }
64
+ 50% { opacity: 0.5; }
65
+ 100% { opacity: 1; }
 
66
  }
67
 
68
+ .controls {
69
+ display: flex;
70
+ gap: 10px;
71
  }
72
 
73
+ button {
74
  padding: 10px 20px;
75
  border: none;
76
+ border-radius: 6px;
 
 
77
  cursor: pointer;
78
+ font-weight: 500;
79
+ transition: all 0.2s;
 
80
  }
81
 
82
+ button:disabled {
83
+ opacity: 0.5;
84
+ cursor: not-allowed;
85
  }
86
 
87
+ .start-btn {
88
+ background: #22c55e;
89
+ color: white;
 
 
90
  }
91
 
92
+ .stop-btn {
93
+ background: #ef4444;
94
+ color: white;
 
 
 
95
  }
96
 
97
+ .preview-container {
98
+ background: black;
99
+ border-radius: 8px;
100
+ overflow: hidden;
101
+ margin-bottom: 20px;
102
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
103
+ position: relative;
104
  }
105
 
106
+ #preview {
107
+ width: 100%;
108
+ aspect-ratio: 16/9;
109
+ object-fit: contain;
110
  }
111
 
112
+ .logs {
113
+ background: white;
114
+ border-radius: 8px;
115
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
116
  overflow: hidden;
 
117
  }
118
 
119
+ .log-header {
120
+ background: #1e293b;
121
+ color: white;
122
+ padding: 15px;
123
+ font-weight: 500;
124
  }
125
 
126
+ .log-entry {
127
+ padding: 20px;
128
+ border-bottom: 1px solid #e2e8f0;
129
+ display: grid;
130
+ grid-template-columns: 300px 1fr;
131
+ gap: 20px;
132
  }
133
 
134
+ .screenshot-container {
135
+ position: relative;
 
 
 
136
  }
137
 
138
+ .screenshot {
139
+ width: 100%;
140
+ border-radius: 4px;
141
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 
 
 
 
142
  }
143
 
144
+ .change-highlight {
145
+ position: absolute;
146
+ border: 2px solid #ef4444;
147
+ background: rgba(239, 68, 68, 0.2);
148
+ pointer-events: none;
149
+ transition: all 0.3s;
150
  }
151
 
152
+ .info-panel {
153
  display: flex;
154
  flex-direction: column;
155
+ gap: 15px;
156
  }
157
 
158
+ .timestamp {
159
+ color: #64748b;
160
+ font-size: 0.9rem;
161
  }
162
 
163
+ .analysis {
164
+ background: #f8fafc;
165
+ padding: 15px;
166
+ border-radius: 6px;
167
+ font-size: 0.95rem;
168
+ line-height: 1.5;
169
  }
170
 
171
+ .analysis h4 {
172
+ margin-bottom: 8px;
173
+ color: #1e293b;
174
+ }
175
+
176
+ .ocr-text {
177
+ font-family: monospace;
178
+ white-space: pre-wrap;
179
+ background: #f1f5f9;
180
+ padding: 10px;
181
+ border-radius: 4px;
182
+ margin-top: 8px;
183
+ }
184
+
185
+ .actions {
186
+ display: flex;
187
+ gap: 10px;
188
+ margin-top: auto;
189
  }
190
 
191
+ .download-btn {
192
+ padding: 8px 16px;
193
+ background: #3b82f6;
194
+ color: white;
195
+ text-decoration: none;
196
+ border-radius: 4px;
197
+ font-size: 0.9rem;
198
+ display: flex;
199
+ align-items: center;
200
+ gap: 6px;
201
+ }
202
+
203
+ .error-message {
204
+ background: #fef2f2;
205
+ color: #ef4444;
206
+ padding: 10px;
207
+ border-radius: 4px;
208
+ margin: 10px 0;
209
+ display: none;
210
+ }
211
+
212
+ @media (max-width: 768px) {
213
+ .log-entry {
214
+ grid-template-columns: 1fr;
215
+ }
216
  }
217
  </style>
218
  </head>
219
  <body>
220
+ <div class="app-container">
221
+ <div class="header">
222
+ <h1>Screen Monitor Pro</h1>
223
+ <p>Advanced screen capture and analysis</p>
 
 
 
 
224
  </div>
225
 
226
+ <div class="status-bar">
227
+ <div class="status-indicator">
228
+ <div class="status-dot" id="statusDot"></div>
229
+ <span id="statusText">Ready</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </div>
231
+ <div class="controls">
232
+ <button class="start-btn" id="startBtn">Start Capture</button>
233
+ <button class="stop-btn" id="stopBtn" disabled>Stop</button>
 
 
 
234
  </div>
235
  </div>
 
236
 
237
+ <div class="error-message" id="errorMessage"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ <div class="preview-container">
240
+ <video id="preview" autoplay></video>
241
+ </div>
242
 
243
+ <div class="logs">
244
+ <div class="log-header">Change Log</div>
245
+ <div id="logContainer"></div>
246
+ </div>
247
+ </div>
248
 
249
+ <script>
250
+ class ScreenAnalyzer {
251
+ constructor() {
252
+ this.mediaStream = null;
253
+ this.captureInterval = null;
254
+ this.lastImageData = null;
255
+ this.worker = null;
256
+ this.isProcessing = false;
257
+
258
+ this.initializeOCR();
259
+ this.bindElements();
260
+ this.bindEvents();
261
  }
 
262
 
263
+ async initializeOCR() {
264
+ try {
265
+ this.worker = await Tesseract.createWorker();
266
+ await this.worker.loadLanguage('eng');
267
+ await this.worker.initialize('eng');
268
+ } catch (error) {
269
+ this.showError('Failed to initialize OCR');
270
+ }
271
+ }
272
 
273
+ bindElements() {
274
+ this.elements = {
275
+ startBtn: document.getElementById('startBtn'),
276
+ stopBtn: document.getElementById('stopBtn'),
277
+ preview: document.getElementById('preview'),
278
+ logContainer: document.getElementById('logContainer'),
279
+ statusDot: document.getElementById('statusDot'),
280
+ statusText: document.getElementById('statusText'),
281
+ errorMessage: document.getElementById('errorMessage')
282
+ };
283
+ }
284
 
285
+ bindEvents() {
286
+ this.elements.startBtn.addEventListener('click', () => this.start());
287
+ this.elements.stopBtn.addEventListener('click', () => this.stop());
288
+ }
289
 
290
+ updateStatus(status, isError = false) {
291
+ this.elements.statusDot.className = `status-dot ${status === 'recording' ? 'active' : ''}`;
292
+ this.elements.statusText.textContent = status.charAt(0).toUpperCase() + status.slice(1);
293
+
294
+ if (isError) {
295
+ this.elements.errorMessage.textContent = status;
296
+ this.elements.errorMessage.style.display = 'block';
297
+ } else {
298
+ this.elements.errorMessage.style.display = 'none';
299
+ }
300
+ }
301
 
302
+ async start() {
303
+ try {
304
+ this.mediaStream = await navigator.mediaDevices.getDisplayMedia({
305
+ video: { cursor: "always" }
306
+ });
307
 
308
+ this.elements.preview.srcObject = this.mediaStream;
309
+ this.elements.startBtn.disabled = true;
310
+ this.elements.stopBtn.disabled = false;
311
+ this.updateStatus('recording');
312
 
313
+ this.captureInterval = setInterval(() => this.capture(), 1000);
314
+ this.mediaStream.getVideoTracks()[0].onended = () => this.stop();
 
 
 
 
 
315
 
316
+ } catch (error) {
317
+ this.showError('Failed to start screen capture');
318
+ }
319
+ }
320
 
321
+ stop() {
322
+ if (this.mediaStream) {
323
+ this.mediaStream.getTracks().forEach(track => track.stop());
324
+ this.elements.preview.srcObject = null;
325
+ }
326
+ clearInterval(this.captureInterval);
327
+ this.elements.startBtn.disabled = false;
328
+ this.elements.stopBtn.disabled = true;
329
+ this.lastImageData = null;
330
+ this.updateStatus('ready');
331
  }
332
 
333
+ async capture() {
334
+ if (this.isProcessing) return;
335
+ this.isProcessing = true;
336
+
337
+ try {
338
+ const canvas = document.createElement('canvas');
339
+ canvas.width = this.elements.preview.videoWidth;
340
+ canvas.height = this.elements.preview.videoHeight;
341
+
342
+ const ctx = canvas.getContext('2d');
343
+ ctx.drawImage(this.elements.preview, 0, 0);
344
+
345
+ const currentImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
346
+ const changes = this.detectChanges(currentImageData);
347
+
348
+ if (changes.length > 0 || !this.lastImageData) {
349
+ const imageUrl = canvas.toDataURL('image/jpeg', 0.8);
350
+ const ocrResults = await this.analyzeChanges(canvas, changes);
351
+ this.addLogEntry(imageUrl, changes, ocrResults);
352
+ }
353
 
354
+ this.lastImageData = currentImageData;
355
+ } catch (error) {
356
+ this.showError('Failed to process capture');
357
+ } finally {
358
+ this.isProcessing = false;
359
+ }
 
 
 
 
 
 
 
 
360
  }
 
361
 
362
+ detectChanges(currentImageData) {
363
+ if (!this.lastImageData) return [];
364
+
365
+ const changes = [];
366
+ const blockSize = 20;
367
+ const threshold = 30;
368
+ const width = currentImageData.width;
369
+ const height = currentImageData.height;
370
+
371
+ for (let y = 0; y < height; y += blockSize) {
372
+ for (let x = 0; x < width; x += blockSize) {
373
+ let diffCount = 0;
374
+ const maxY = Math.min(y + blockSize, height);
375
+ const maxX = Math.min(x + blockSize, width);
376
+
377
+ for (let py = y; py < maxY; py++) {
378
+ for (let px = x; px < maxX; px++) {
379
+ const i = (py * width + px) * 4;
380
+ if (Math.abs(currentImageData.data[i] - this.lastImageData.data[i]) > threshold ||
381
+ Math.abs(currentImageData.data[i + 1] - this.lastImageData.data[i + 1]) > threshold ||
382
+ Math.abs(currentImageData.data[i + 2] - this.lastImageData.data[i + 2]) > threshold) {
383
+ diffCount++;
384
+ }
385
+ }
386
+ }
 
 
 
 
 
 
387
 
388
+ if (diffCount > (blockSize * blockSize * 0.3)) {
389
+ changes.push({
390
+ x: x / width * 100,
391
+ y: y / height * 100,
392
+ width: Math.min(blockSize / width * 100, 100 - x / width * 100),
393
+ height: Math.min(blockSize / height * 100, 100 - y / height * 100),
394
+ pixels: {x, y, width: maxX - x, height: maxY - y}
395
+ });
396
+ }
397
+ }
398
  }
 
 
399
 
400
+ return changes;
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
 
 
 
 
 
 
 
 
402
 
403
+ async analyzeChanges(canvas, changes) {
404
+ const results = [];
405
+
406
+ for (const change of changes) {
407
+ const tempCanvas = document.createElement('canvas');
408
+ tempCanvas.width = change.pixels.width;
409
+ tempCanvas.height = change.pixels.height;
410
+ const tempCtx = tempCanvas.getContext('2d');
411
+
412
+ tempCtx.drawImage(
413
+ canvas,
414
+ change.pixels.x, change.pixels.y,
415
+ change.pixels.width, change.pixels.height,
416
+ 0, 0,
417
+ change.pixels.width, change.pixels.height
418
+ );
419
+
420
+ try {
421
+ const result = await this.worker.recognize(tempCanvas);
422
+ if (result.data.text.trim()) {
423
+ results.push({
424
+ text: result.data.text.trim(),
425
+ confidence: result.data.confidence,
426
+ region: change
427
+ });
428
  }
429
+ } catch (error) {
430
+ console.error('OCR Error:', error);
431
  }
432
  }
 
 
433
 
434
+ return results;
 
 
 
 
 
 
 
 
 
 
435
  }
436
 
437
+ addLogEntry(imageUrl, changes, ocrResults) {
438
+ const logEntry = document.createElement('div');
439
+ logEntry.className = 'log-entry';
440
+
441
+ const timestamp = new Date().toLocaleString();
442
+
443
+ logEntry.innerHTML = `
444
+ <div class="screenshot-container">
445
+ <img class="screenshot" src="${imageUrl}" alt="Screenshot">
446
+ ${changes.map((change, index) => `
447
+ <div class="change-highlight" style="
448
+ left: ${change.x}%;
449
+ top: ${change.y}%;
450
+ width: ${change.width}%;
451
+ height: ${change.height}%;
452
+ "></div>
453
+ `).join('')}
454
+ </div>
455
+ <div class="info-panel">
456
+ <div class="timestamp">📅 ${timestamp}</div>
457
+ <div class="analysis">
458
+ <h4>Changes Detected</h4>
459
+ <p>${changes.length} regions changed</p>
460
+ ${ocrResults.length > 0 ? `
461
+ <h4>Text Content</h4>
462
+ <div class="ocr-text">
463
+ ${ocrResults.map(result =>
464
+ `[Region ${Math.round(result.confidence)}% confidence]\n${result.text}`
465
+ ).join('\n\n')}
466
+ </div>
467
+ ` : ''}
468
+ </div>
469
+ <div class="actions">
470
+ <a href="${imageUrl}" download="screenshot-${Date.now()}.jpg"
471
+ class="download-btn">
472
+ 📸 Download Screenshot
473
+ </a>
474
+ </div>
475
+ </div>
476
+ `;
477
+
478
+ this.elements.logContainer.insertBefore(logEntry, this.elements.logContainer.firstChild);
479
+ }
480
 
481
+ showError(message) {
482
+ this.updateStatus(message, true);
483
+ console.error(message);
484
+ }
485
+
486
+ async cleanup() {
487
+ if (this.worker) {
488
+ await this.worker.terminate();
489
+ }
490
+ this.stop();
491
+ }
492
  }
493
 
494
+ // Initialize the application
495
+ const analyzer = new ScreenAnalyzer();
496
+ window.addEventListener('beforeunload', () => analyzer.cleanup());
497
  </script>
498
  </body>
499
  </html>