@elizaos/plugin-vision 1.2.1

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 (106) hide show
  1. package/.npmignore +5 -0
  2. package/README.md +270 -0
  3. package/build.config.ts +70 -0
  4. package/dist/action.d.ts +8 -0
  5. package/dist/action.js +1212 -0
  6. package/dist/action.js.map +1 -0
  7. package/dist/audio-capture-stream.d.ts +42 -0
  8. package/dist/audio-capture-stream.js +516 -0
  9. package/dist/audio-capture-stream.js.map +1 -0
  10. package/dist/audio-capture.d.ts +25 -0
  11. package/dist/audio-capture.js +412 -0
  12. package/dist/audio-capture.js.map +1 -0
  13. package/dist/basic.test.d.ts +1 -0
  14. package/dist/basic.test.js +97 -0
  15. package/dist/basic.test.js.map +1 -0
  16. package/dist/config.d.ts +73 -0
  17. package/dist/config.js +254 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/entity-tracker.d.ts +32 -0
  20. package/dist/entity-tracker.js +361 -0
  21. package/dist/entity-tracker.js.map +1 -0
  22. package/dist/errors.d.ts +67 -0
  23. package/dist/errors.js +395 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/face-recognition.d.ts +31 -0
  26. package/dist/face-recognition.js +332 -0
  27. package/dist/face-recognition.js.map +1 -0
  28. package/dist/florence2-local.d.ts +25 -0
  29. package/dist/florence2-local.js +280 -0
  30. package/dist/florence2-local.js.map +1 -0
  31. package/dist/florence2-model.d.ts +36 -0
  32. package/dist/florence2-model.js +503 -0
  33. package/dist/florence2-model.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.js +73 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/ocr-service-real.d.ts +32 -0
  38. package/dist/ocr-service-real.js +396 -0
  39. package/dist/ocr-service-real.js.map +1 -0
  40. package/dist/ocr-service.d.ts +28 -0
  41. package/dist/ocr-service.js +216 -0
  42. package/dist/ocr-service.js.map +1 -0
  43. package/dist/provider.d.ts +2 -0
  44. package/dist/provider.js +285 -0
  45. package/dist/provider.js.map +1 -0
  46. package/dist/screen-capture.d.ts +16 -0
  47. package/dist/screen-capture.js +302 -0
  48. package/dist/screen-capture.js.map +1 -0
  49. package/dist/service.d.ts +73 -0
  50. package/dist/service.js +1662 -0
  51. package/dist/service.js.map +1 -0
  52. package/dist/tests/e2e/index.d.ts +8 -0
  53. package/dist/tests/e2e/index.js +33 -0
  54. package/dist/tests/e2e/index.js.map +1 -0
  55. package/dist/tests/e2e/run-local.d.ts +2 -0
  56. package/dist/tests/e2e/run-local.js +166 -0
  57. package/dist/tests/e2e/run-local.js.map +1 -0
  58. package/dist/tests/e2e/screen-vision.d.ts +11 -0
  59. package/dist/tests/e2e/screen-vision.js +384 -0
  60. package/dist/tests/e2e/screen-vision.js.map +1 -0
  61. package/dist/tests/e2e/vision-autonomy.d.ts +11 -0
  62. package/dist/tests/e2e/vision-autonomy.js +375 -0
  63. package/dist/tests/e2e/vision-autonomy.js.map +1 -0
  64. package/dist/tests/e2e/vision-basic.d.ts +11 -0
  65. package/dist/tests/e2e/vision-basic.js +434 -0
  66. package/dist/tests/e2e/vision-basic.js.map +1 -0
  67. package/dist/tests/e2e/vision-capture-log.d.ts +11 -0
  68. package/dist/tests/e2e/vision-capture-log.js +302 -0
  69. package/dist/tests/e2e/vision-capture-log.js.map +1 -0
  70. package/dist/tests/e2e/vision-runtime.d.ts +11 -0
  71. package/dist/tests/e2e/vision-runtime.js +357 -0
  72. package/dist/tests/e2e/vision-runtime.js.map +1 -0
  73. package/dist/tests/e2e/vision-worker-tests.d.ts +11 -0
  74. package/dist/tests/e2e/vision-worker-tests.js +466 -0
  75. package/dist/tests/e2e/vision-worker-tests.js.map +1 -0
  76. package/dist/tests/test-pattern-generator.d.ts +40 -0
  77. package/dist/tests/test-pattern-generator.js +191 -0
  78. package/dist/tests/test-pattern-generator.js.map +1 -0
  79. package/dist/tests.d.ts +3 -0
  80. package/dist/tests.js +11 -0
  81. package/dist/tests.js.map +1 -0
  82. package/dist/types.d.ts +222 -0
  83. package/dist/types.js +16 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/vision-models.d.ts +47 -0
  86. package/dist/vision-models.js +501 -0
  87. package/dist/vision-models.js.map +1 -0
  88. package/dist/vision-worker-manager.d.ts +61 -0
  89. package/dist/vision-worker-manager.js +668 -0
  90. package/dist/vision-worker-manager.js.map +1 -0
  91. package/dist/workers/florence2-worker-simple.d.ts +13 -0
  92. package/dist/workers/florence2-worker-simple.js +121 -0
  93. package/dist/workers/florence2-worker-simple.js.map +1 -0
  94. package/dist/workers/florence2-worker.d.ts +1 -0
  95. package/dist/workers/florence2-worker.js +328 -0
  96. package/dist/workers/florence2-worker.js.map +1 -0
  97. package/dist/workers/ocr-worker.d.ts +1 -0
  98. package/dist/workers/ocr-worker.js +354 -0
  99. package/dist/workers/ocr-worker.js.map +1 -0
  100. package/dist/workers/screen-capture-worker.d.ts +1 -0
  101. package/dist/workers/screen-capture-worker.js +427 -0
  102. package/dist/workers/screen-capture-worker.js.map +1 -0
  103. package/dist/workers/worker-logger.d.ts +9 -0
  104. package/dist/workers/worker-logger.js +95 -0
  105. package/dist/workers/worker-logger.js.map +1 -0
  106. package/package.json +100 -0
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var worker_threads_1 = require("worker_threads");
40
+ var worker_logger_1 = require("./worker-logger");
41
+ var ocr_service_1 = require("../ocr-service");
42
+ var sharp_1 = require("sharp");
43
+ var OCRWorker = /** @class */ (function () {
44
+ function OCRWorker(config, sharedBuffer, resultsBuffer) {
45
+ this.isRunning = true;
46
+ this.frameCount = 0;
47
+ this.lastFPSReport = Date.now();
48
+ this.lastFrameId = -1;
49
+ // Atomic indices for input buffer
50
+ this.FRAME_ID_INDEX = 0;
51
+ this.WRITE_LOCK_INDEX = 1;
52
+ this.WIDTH_INDEX = 2;
53
+ this.HEIGHT_INDEX = 3;
54
+ this.DISPLAY_INDEX = 4;
55
+ this.TIMESTAMP_INDEX = 5;
56
+ this.DATA_OFFSET = 24;
57
+ // Results buffer structure
58
+ this.RESULTS_HEADER_SIZE = 16;
59
+ this.MAX_TEXT_LENGTH = 65536; // 64KB for text
60
+ this.config = config;
61
+ this.sharedBuffer = sharedBuffer;
62
+ this.dataView = new DataView(sharedBuffer);
63
+ this.atomicState = new Int32Array(sharedBuffer, 0, 6);
64
+ this.resultsBuffer = resultsBuffer;
65
+ this.resultsView = new DataView(resultsBuffer);
66
+ this.ocrService = new ocr_service_1.OCRService();
67
+ }
68
+ OCRWorker.prototype.initialize = function () {
69
+ return __awaiter(this, void 0, void 0, function () {
70
+ return __generator(this, function (_a) {
71
+ switch (_a.label) {
72
+ case 0: return [4 /*yield*/, this.ocrService.initialize()];
73
+ case 1:
74
+ _a.sent();
75
+ worker_logger_1.logger.info('[OCRWorker] Initialized and ready');
76
+ return [2 /*return*/];
77
+ }
78
+ });
79
+ });
80
+ };
81
+ OCRWorker.prototype.run = function () {
82
+ return __awaiter(this, void 0, void 0, function () {
83
+ var currentFrameId, now, fps, error_1;
84
+ return __generator(this, function (_a) {
85
+ switch (_a.label) {
86
+ case 0: return [4 /*yield*/, this.initialize()];
87
+ case 1:
88
+ _a.sent();
89
+ worker_logger_1.logger.info('[OCRWorker] Starting OCR loop...');
90
+ _a.label = 2;
91
+ case 2:
92
+ if (!this.isRunning) return [3 /*break*/, 11];
93
+ _a.label = 3;
94
+ case 3:
95
+ _a.trys.push([3, 8, , 10]);
96
+ currentFrameId = Atomics.load(this.atomicState, this.FRAME_ID_INDEX);
97
+ if (!(currentFrameId > this.lastFrameId)) return [3 /*break*/, 5];
98
+ return [4 /*yield*/, this.processFrame()];
99
+ case 4:
100
+ _a.sent();
101
+ this.lastFrameId = currentFrameId;
102
+ this.frameCount++;
103
+ now = Date.now();
104
+ if (now - this.lastFPSReport >= 1000) {
105
+ fps = this.frameCount / ((now - this.lastFPSReport) / 1000);
106
+ worker_logger_1.logger.info("[OCRWorker] OCR FPS: ".concat(fps.toFixed(2)));
107
+ worker_threads_1.parentPort === null || worker_threads_1.parentPort === void 0 ? void 0 : worker_threads_1.parentPort.postMessage({
108
+ type: 'fps',
109
+ fps: fps,
110
+ frameCount: this.frameCount,
111
+ });
112
+ this.frameCount = 0;
113
+ this.lastFPSReport = now;
114
+ }
115
+ return [3 /*break*/, 7];
116
+ case 5:
117
+ // No new frame, brief yield
118
+ return [4 /*yield*/, new Promise(function (resolve) { return setImmediate(resolve); })];
119
+ case 6:
120
+ // No new frame, brief yield
121
+ _a.sent();
122
+ _a.label = 7;
123
+ case 7: return [3 /*break*/, 10];
124
+ case 8:
125
+ error_1 = _a.sent();
126
+ worker_logger_1.logger.error('[OCRWorker] Processing error:', error_1);
127
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 100); })];
128
+ case 9:
129
+ _a.sent();
130
+ return [3 /*break*/, 10];
131
+ case 10: return [3 /*break*/, 2];
132
+ case 11: return [2 /*return*/];
133
+ }
134
+ });
135
+ });
136
+ };
137
+ OCRWorker.prototype.processFrame = function () {
138
+ return __awaiter(this, void 0, void 0, function () {
139
+ var metadata, results, screenBuffer, ocrResult, error_2, _i, _a, region, regionBuffer, ocrResult, error_3, totalText, totalBlocks;
140
+ return __generator(this, function (_b) {
141
+ switch (_b.label) {
142
+ case 0:
143
+ metadata = {
144
+ frameId: Atomics.load(this.atomicState, this.FRAME_ID_INDEX),
145
+ width: Atomics.load(this.atomicState, this.WIDTH_INDEX),
146
+ height: Atomics.load(this.atomicState, this.HEIGHT_INDEX),
147
+ displayIndex: Atomics.load(this.atomicState, this.DISPLAY_INDEX),
148
+ timestamp: Atomics.load(this.atomicState, this.TIMESTAMP_INDEX),
149
+ };
150
+ results = [];
151
+ if (!this.config.processFullScreen) return [3 /*break*/, 5];
152
+ _b.label = 1;
153
+ case 1:
154
+ _b.trys.push([1, 4, , 5]);
155
+ return [4 /*yield*/, this.extractFullScreenBuffer(metadata)];
156
+ case 2:
157
+ screenBuffer = _b.sent();
158
+ return [4 /*yield*/, this.ocrService.extractText(screenBuffer)];
159
+ case 3:
160
+ ocrResult = _b.sent();
161
+ results.push(ocrResult);
162
+ worker_logger_1.logger.debug("[OCRWorker] Full screen OCR: ".concat(ocrResult.fullText.length, " chars"));
163
+ return [3 /*break*/, 5];
164
+ case 4:
165
+ error_2 = _b.sent();
166
+ worker_logger_1.logger.error('[OCRWorker] Full screen OCR failed:', error_2);
167
+ return [3 /*break*/, 5];
168
+ case 5:
169
+ if (!(this.config.textRegions && this.config.textRegions.length > 0)) return [3 /*break*/, 12];
170
+ _i = 0, _a = this.config.textRegions;
171
+ _b.label = 6;
172
+ case 6:
173
+ if (!(_i < _a.length)) return [3 /*break*/, 12];
174
+ region = _a[_i];
175
+ _b.label = 7;
176
+ case 7:
177
+ _b.trys.push([7, 10, , 11]);
178
+ return [4 /*yield*/, this.extractRegionBuffer(region, metadata)];
179
+ case 8:
180
+ regionBuffer = _b.sent();
181
+ return [4 /*yield*/, this.ocrService.extractText(regionBuffer)];
182
+ case 9:
183
+ ocrResult = _b.sent();
184
+ results.push(ocrResult);
185
+ worker_logger_1.logger.debug("[OCRWorker] Region OCR (".concat(region.x, ",").concat(region.y, "): ").concat(ocrResult.fullText.length, " chars"));
186
+ return [3 /*break*/, 11];
187
+ case 10:
188
+ error_3 = _b.sent();
189
+ worker_logger_1.logger.error('[OCRWorker] Region OCR failed:', error_3);
190
+ return [3 /*break*/, 11];
191
+ case 11:
192
+ _i++;
193
+ return [3 /*break*/, 6];
194
+ case 12:
195
+ // Write combined results to buffer
196
+ return [4 /*yield*/, this.writeResultsToBuffer(results, metadata.frameId)];
197
+ case 13:
198
+ // Write combined results to buffer
199
+ _b.sent();
200
+ totalText = results.map(function (r) { return r.fullText; }).join('\n');
201
+ totalBlocks = results.reduce(function (sum, r) { return sum + r.blocks.length; }, 0);
202
+ worker_threads_1.parentPort === null || worker_threads_1.parentPort === void 0 ? void 0 : worker_threads_1.parentPort.postMessage({
203
+ type: 'ocr_complete',
204
+ frameId: metadata.frameId,
205
+ displayIndex: metadata.displayIndex,
206
+ textLength: totalText.length,
207
+ blockCount: totalBlocks,
208
+ hasText: totalText.length > 0,
209
+ });
210
+ return [2 /*return*/];
211
+ }
212
+ });
213
+ });
214
+ };
215
+ OCRWorker.prototype.extractFullScreenBuffer = function (metadata) {
216
+ return __awaiter(this, void 0, void 0, function () {
217
+ var bytesPerPixel, totalPixels, totalBytes, screenData, i, pngBuffer;
218
+ return __generator(this, function (_a) {
219
+ switch (_a.label) {
220
+ case 0:
221
+ bytesPerPixel = 4;
222
+ totalPixels = metadata.width * metadata.height;
223
+ totalBytes = totalPixels * bytesPerPixel;
224
+ screenData = Buffer.allocUnsafe(totalBytes);
225
+ // Copy all data
226
+ for (i = 0; i < totalBytes; i++) {
227
+ screenData[i] = this.dataView.getUint8(this.DATA_OFFSET + i);
228
+ }
229
+ return [4 /*yield*/, (0, sharp_1.default)(screenData, {
230
+ raw: {
231
+ width: metadata.width,
232
+ height: metadata.height,
233
+ channels: 4,
234
+ },
235
+ })
236
+ .png()
237
+ .toBuffer()];
238
+ case 1:
239
+ pngBuffer = _a.sent();
240
+ return [2 /*return*/, pngBuffer];
241
+ }
242
+ });
243
+ });
244
+ };
245
+ OCRWorker.prototype.extractRegionBuffer = function (region, metadata) {
246
+ return __awaiter(this, void 0, void 0, function () {
247
+ var bytesPerPixel, rowStride, x, y, width, height, regionData, row, sourceY, sourceOffset, destOffset, i, pngBuffer;
248
+ return __generator(this, function (_a) {
249
+ switch (_a.label) {
250
+ case 0:
251
+ bytesPerPixel = 4;
252
+ rowStride = metadata.width * bytesPerPixel;
253
+ x = Math.max(0, Math.min(region.x, metadata.width - 1));
254
+ y = Math.max(0, Math.min(region.y, metadata.height - 1));
255
+ width = Math.min(region.width, metadata.width - x);
256
+ height = Math.min(region.height, metadata.height - y);
257
+ regionData = Buffer.allocUnsafe(width * height * bytesPerPixel);
258
+ // Copy region data row by row
259
+ for (row = 0; row < height; row++) {
260
+ sourceY = y + row;
261
+ sourceOffset = this.DATA_OFFSET + sourceY * rowStride + x * bytesPerPixel;
262
+ destOffset = row * width * bytesPerPixel;
263
+ for (i = 0; i < width * bytesPerPixel; i++) {
264
+ regionData[destOffset + i] = this.dataView.getUint8(sourceOffset + i);
265
+ }
266
+ }
267
+ return [4 /*yield*/, (0, sharp_1.default)(regionData, {
268
+ raw: {
269
+ width: width,
270
+ height: height,
271
+ channels: 4,
272
+ },
273
+ })
274
+ .png()
275
+ .toBuffer()];
276
+ case 1:
277
+ pngBuffer = _a.sent();
278
+ return [2 /*return*/, pngBuffer];
279
+ }
280
+ });
281
+ });
282
+ };
283
+ OCRWorker.prototype.writeResultsToBuffer = function (results, frameId) {
284
+ return __awaiter(this, void 0, void 0, function () {
285
+ var combinedResult, resultJson, resultBytes, offset, dataOffset, i;
286
+ return __generator(this, function (_a) {
287
+ combinedResult = {
288
+ frameId: frameId,
289
+ timestamp: Date.now(),
290
+ fullText: results.map(function (r) { return r.fullText; }).join('\n'),
291
+ blocks: results.flatMap(function (r) { return r.blocks; }),
292
+ regions: results.length,
293
+ };
294
+ resultJson = JSON.stringify(combinedResult);
295
+ resultBytes = Buffer.from(resultJson, 'utf-8');
296
+ offset = this.RESULTS_HEADER_SIZE;
297
+ // Write length
298
+ this.resultsView.setUint32(offset, resultBytes.length, true);
299
+ // Write frame ID
300
+ this.resultsView.setUint32(offset + 4, frameId, true);
301
+ // Write timestamp
302
+ this.resultsView.setFloat64(offset + 8, Date.now(), true);
303
+ dataOffset = offset + 16;
304
+ for (i = 0; i < Math.min(resultBytes.length, this.MAX_TEXT_LENGTH); i++) {
305
+ this.resultsView.setUint8(dataOffset + i, resultBytes[i]);
306
+ }
307
+ return [2 /*return*/];
308
+ });
309
+ });
310
+ };
311
+ OCRWorker.prototype.stop = function () {
312
+ this.isRunning = false;
313
+ };
314
+ OCRWorker.prototype.dispose = function () {
315
+ return __awaiter(this, void 0, void 0, function () {
316
+ return __generator(this, function (_a) {
317
+ switch (_a.label) {
318
+ case 0: return [4 /*yield*/, this.ocrService.dispose()];
319
+ case 1:
320
+ _a.sent();
321
+ return [2 /*return*/];
322
+ }
323
+ });
324
+ });
325
+ };
326
+ OCRWorker.prototype.updateTextRegions = function (regions) {
327
+ this.config.textRegions = regions;
328
+ };
329
+ return OCRWorker;
330
+ }());
331
+ // Worker entry point
332
+ if (worker_threads_1.parentPort) {
333
+ var config = worker_threads_1.workerData.config, sharedBuffer = worker_threads_1.workerData.sharedBuffer, resultsBuffer = worker_threads_1.workerData.resultsBuffer;
334
+ var worker_1 = new OCRWorker(config, sharedBuffer, resultsBuffer);
335
+ // Handle messages from main thread
336
+ worker_threads_1.parentPort.on('message', function (msg) {
337
+ if (msg.type === 'stop') {
338
+ worker_1.stop();
339
+ worker_1.dispose().then(function () {
340
+ worker_threads_1.parentPort === null || worker_threads_1.parentPort === void 0 ? void 0 : worker_threads_1.parentPort.postMessage({ type: 'stopped' });
341
+ });
342
+ }
343
+ else if (msg.type === 'update_regions') {
344
+ worker_1.updateTextRegions(msg.regions);
345
+ }
346
+ });
347
+ // Run the worker
348
+ worker_1.run().catch(function (error) {
349
+ worker_logger_1.logger.error('[OCRWorker] Fatal error:', error);
350
+ worker_threads_1.parentPort === null || worker_threads_1.parentPort === void 0 ? void 0 : worker_threads_1.parentPort.postMessage({ type: 'error', error: error.message });
351
+ process.exit(1);
352
+ });
353
+ }
354
+ //# sourceMappingURL=ocr-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocr-worker.js","sourceRoot":"","sources":["../../src/workers/ocr-worker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAwD;AACxD,iDAAyC;AACzC,8CAA4C;AAE5C,+BAA0B;AAgB1B;IA0BE,mBACE,MAAoB,EACpB,YAA+B,EAC/B,aAAgC;QArB1B,cAAS,GAAG,IAAI,CAAC;QACjB,eAAU,GAAG,CAAC,CAAC;QACf,kBAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,gBAAW,GAAG,CAAC,CAAC,CAAC;QAEzB,kCAAkC;QACjB,mBAAc,GAAG,CAAC,CAAC;QACnB,qBAAgB,GAAG,CAAC,CAAC;QACrB,gBAAW,GAAG,CAAC,CAAC;QAChB,iBAAY,GAAG,CAAC,CAAC;QACjB,kBAAa,GAAG,CAAC,CAAC;QAClB,oBAAe,GAAG,CAAC,CAAC;QACpB,gBAAW,GAAG,EAAE,CAAC;QAElC,2BAA2B;QACV,wBAAmB,GAAG,EAAE,CAAC;QACzB,oBAAe,GAAG,KAAK,CAAC,CAAC,gBAAgB;QAOxD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,IAAI,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,IAAI,wBAAU,EAAE,CAAC;IACrC,CAAC;IAEK,8BAAU,GAAhB;;;;4BACE,qBAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,EAAA;;wBAAlC,SAAkC,CAAC;wBACnC,sBAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;;;;;KAClD;IAEK,uBAAG,GAAT;;;;;4BACE,qBAAM,IAAI,CAAC,UAAU,EAAE,EAAA;;wBAAvB,SAAuB,CAAC;wBAExB,sBAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;;;6BAEzC,IAAI,CAAC,SAAS;;;;wBAGX,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;6BAEvE,CAAA,cAAc,GAAG,IAAI,CAAC,WAAW,CAAA,EAAjC,wBAAiC;wBACnC,qBAAM,IAAI,CAAC,YAAY,EAAE,EAAA;;wBAAzB,SAAyB,CAAC;wBAC1B,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC;wBAClC,IAAI,CAAC,UAAU,EAAE,CAAC;wBAGZ,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBACvB,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;4BAC/B,GAAG,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;4BAClE,sBAAM,CAAC,IAAI,CAAC,+BAAwB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;4BAEtD,2BAAU,aAAV,2BAAU,uBAAV,2BAAU,CAAE,WAAW,CAAC;gCACtB,IAAI,EAAE,KAAK;gCACX,GAAG,KAAA;gCACH,UAAU,EAAE,IAAI,CAAC,UAAU;6BAC5B,CAAC,CAAC;4BAEH,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;4BACpB,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC;wBAC3B,CAAC;;;oBAED,4BAA4B;oBAC5B,qBAAM,IAAI,OAAO,CAAC,UAAC,OAAO,IAAK,OAAA,YAAY,CAAC,OAAO,CAAC,EAArB,CAAqB,CAAC,EAAA;;wBADrD,4BAA4B;wBAC5B,SAAqD,CAAC;;;;;wBAGxD,sBAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,OAAK,CAAC,CAAC;wBACrD,qBAAM,IAAI,OAAO,CAAC,UAAC,OAAO,IAAK,OAAA,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,EAAxB,CAAwB,CAAC,EAAA;;wBAAxD,SAAwD,CAAC;;;;;;;KAG9D;IAEa,gCAAY,GAA1B;;;;;;wBAEQ,QAAQ,GAAmB;4BAC/B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC;4BAC5D,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;4BACvD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC;4BACzD,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC;4BAChE,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC;yBAChE,CAAC;wBAEI,OAAO,GAAgB,EAAE,CAAC;6BAE5B,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAA7B,wBAA6B;;;;wBAGR,qBAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,EAAA;;wBAA3D,YAAY,GAAG,SAA4C;wBAC/C,qBAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,EAAA;;wBAA3D,SAAS,GAAG,SAA+C;wBACjE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBAExB,sBAAM,CAAC,KAAK,CAAC,uCAAgC,SAAS,CAAC,QAAQ,CAAC,MAAM,WAAQ,CAAC,CAAC;;;;wBAEhF,sBAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE,OAAK,CAAC,CAAC;;;6BAK3D,CAAA,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAA,EAA7D,yBAA6D;8BACnB,EAAvB,KAAA,IAAI,CAAC,MAAM,CAAC,WAAW;;;6BAAvB,CAAA,cAAuB,CAAA;wBAAjC,MAAM;;;;wBAEQ,qBAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAA;;wBAA/D,YAAY,GAAG,SAAgD;wBACnD,qBAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,EAAA;;wBAA3D,SAAS,GAAG,SAA+C;wBACjE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBAExB,sBAAM,CAAC,KAAK,CACV,kCAA2B,MAAM,CAAC,CAAC,cAAI,MAAM,CAAC,CAAC,gBAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,WAAQ,CACvF,CAAC;;;;wBAEF,sBAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,OAAK,CAAC,CAAC;;;wBAVrC,IAAuB,CAAA;;;oBAe9C,mCAAmC;oBACnC,qBAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAA;;wBAD1D,mCAAmC;wBACnC,SAA0D,CAAC;wBAGrD,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,QAAQ,EAAV,CAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBACtD,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,UAAC,GAAG,EAAE,CAAC,IAAK,OAAA,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAArB,CAAqB,EAAE,CAAC,CAAC,CAAC;wBAEzE,2BAAU,aAAV,2BAAU,uBAAV,2BAAU,CAAE,WAAW,CAAC;4BACtB,IAAI,EAAE,cAAc;4BACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;4BACzB,YAAY,EAAE,QAAQ,CAAC,YAAY;4BACnC,UAAU,EAAE,SAAS,CAAC,MAAM;4BAC5B,UAAU,EAAE,WAAW;4BACvB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;yBAC9B,CAAC,CAAC;;;;;KACJ;IAEa,2CAAuB,GAArC,UAAsC,QAAwB;;;;;;wBACtD,aAAa,GAAG,CAAC,CAAC;wBAClB,WAAW,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;wBAC/C,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;wBAGzC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;wBAElD,gBAAgB;wBAChB,KAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;4BACpC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;wBAC/D,CAAC;wBAGiB,qBAAM,IAAA,eAAK,EAAC,UAAU,EAAE;gCACxC,GAAG,EAAE;oCACH,KAAK,EAAE,QAAQ,CAAC,KAAK;oCACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;oCACvB,QAAQ,EAAE,CAAC;iCACZ;6BACF,CAAC;iCACC,GAAG,EAAE;iCACL,QAAQ,EAAE,EAAA;;wBARP,SAAS,GAAG,SAQL;wBAEb,sBAAO,SAAS,EAAC;;;;KAClB;IAEa,uCAAmB,GAAjC,UACE,MAA+D,EAC/D,QAAwB;;;;;;wBAElB,aAAa,GAAG,CAAC,CAAC;wBAClB,SAAS,GAAG,QAAQ,CAAC,KAAK,GAAG,aAAa,CAAC;wBAG3C,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;wBACxD,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;wBACzD,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;wBACnD,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;wBAGtD,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,GAAG,MAAM,GAAG,aAAa,CAAC,CAAC;wBAEtE,8BAA8B;wBAC9B,KAAS,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;4BAChC,OAAO,GAAG,CAAC,GAAG,GAAG,CAAC;4BAClB,YAAY,GAAG,IAAI,CAAC,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,CAAC,GAAG,aAAa,CAAC;4BAC1E,UAAU,GAAG,GAAG,GAAG,KAAK,GAAG,aAAa,CAAC;4BAE/C,KAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;gCAC/C,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;4BACxE,CAAC;wBACH,CAAC;wBAGiB,qBAAM,IAAA,eAAK,EAAC,UAAU,EAAE;gCACxC,GAAG,EAAE;oCACH,KAAK,OAAA;oCACL,MAAM,QAAA;oCACN,QAAQ,EAAE,CAAC;iCACZ;6BACF,CAAC;iCACC,GAAG,EAAE;iCACL,QAAQ,EAAE,EAAA;;wBARP,SAAS,GAAG,SAQL;wBAEb,sBAAO,SAAS,EAAC;;;;KAClB;IAEa,wCAAoB,GAAlC,UAAmC,OAAoB,EAAE,OAAe;;;;gBAEhE,cAAc,GAAG;oBACrB,OAAO,SAAA;oBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;oBACrB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,QAAQ,EAAV,CAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBACnD,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,MAAM,EAAR,CAAQ,CAAC;oBACxC,OAAO,EAAE,OAAO,CAAC,MAAM;iBACxB,CAAC;gBAEI,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;gBAC5C,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAG/C,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC;gBAExC,eAAe;gBACf,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAE7D,iBAAiB;gBACjB,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;gBAEtD,kBAAkB;gBAClB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;gBAGpD,UAAU,GAAG,MAAM,GAAG,EAAE,CAAC;gBAC/B,KAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC5E,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5D,CAAC;;;;KACF;IAED,wBAAI,GAAJ;QACE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAEK,2BAAO,GAAb;;;;4BACE,qBAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,EAAA;;wBAA/B,SAA+B,CAAC;;;;;KACjC;IAED,qCAAiB,GAAjB,UAAkB,OAAuE;QACvF,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;IACpC,CAAC;IACH,gBAAC;AAAD,CAAC,AAhQD,IAgQC;AAED,qBAAqB;AACrB,IAAI,2BAAU,EAAE,CAAC;IACP,IAAA,MAAM,GAAkC,2BAAU,OAA5C,EAAE,YAAY,GAAoB,2BAAU,aAA9B,EAAE,aAAa,GAAK,2BAAU,cAAf,CAAgB;IAC3D,IAAM,QAAM,GAAG,IAAI,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;IAElE,mCAAmC;IACnC,2BAAU,CAAC,EAAE,CAAC,SAAS,EAAE,UAAC,GAAG;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACxB,QAAM,CAAC,IAAI,EAAE,CAAC;YACd,QAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC;gBACpB,2BAAU,aAAV,2BAAU,uBAAV,2BAAU,CAAE,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACzC,QAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,iBAAiB;IACjB,QAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,UAAC,KAAK;QACvB,sBAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QAChD,2BAAU,aAAV,2BAAU,uBAAV,2BAAU,CAAE,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { parentPort, workerData } from 'worker_threads';\nimport { logger } from './worker-logger';\nimport { OCRService } from '../ocr-service';\nimport type { OCRResult } from '../types';\nimport sharp from 'sharp';\n\ninterface WorkerConfig {\n processFullScreen: boolean;\n tileSize: number;\n textRegions?: Array<{ x: number; y: number; width: number; height: number }>;\n}\n\ninterface SharedMetadata {\n frameId: number;\n width: number;\n height: number;\n displayIndex: number;\n timestamp: number;\n}\n\nclass OCRWorker {\n private config: WorkerConfig;\n private sharedBuffer: SharedArrayBuffer;\n private dataView: DataView;\n private atomicState: Int32Array;\n private resultsBuffer: SharedArrayBuffer;\n private resultsView: DataView;\n private ocrService: OCRService;\n private isRunning = true;\n private frameCount = 0;\n private lastFPSReport = Date.now();\n private lastFrameId = -1;\n\n // Atomic indices for input buffer\n private readonly FRAME_ID_INDEX = 0;\n private readonly WRITE_LOCK_INDEX = 1;\n private readonly WIDTH_INDEX = 2;\n private readonly HEIGHT_INDEX = 3;\n private readonly DISPLAY_INDEX = 4;\n private readonly TIMESTAMP_INDEX = 5;\n private readonly DATA_OFFSET = 24;\n\n // Results buffer structure\n private readonly RESULTS_HEADER_SIZE = 16;\n private readonly MAX_TEXT_LENGTH = 65536; // 64KB for text\n\n constructor(\n config: WorkerConfig,\n sharedBuffer: SharedArrayBuffer,\n resultsBuffer: SharedArrayBuffer\n ) {\n this.config = config;\n this.sharedBuffer = sharedBuffer;\n this.dataView = new DataView(sharedBuffer);\n this.atomicState = new Int32Array(sharedBuffer, 0, 6);\n this.resultsBuffer = resultsBuffer;\n this.resultsView = new DataView(resultsBuffer);\n this.ocrService = new OCRService();\n }\n\n async initialize(): Promise<void> {\n await this.ocrService.initialize();\n logger.info('[OCRWorker] Initialized and ready');\n }\n\n async run(): Promise<void> {\n await this.initialize();\n\n logger.info('[OCRWorker] Starting OCR loop...');\n\n while (this.isRunning) {\n try {\n // Check for new frame\n const currentFrameId = Atomics.load(this.atomicState, this.FRAME_ID_INDEX);\n\n if (currentFrameId > this.lastFrameId) {\n await this.processFrame();\n this.lastFrameId = currentFrameId;\n this.frameCount++;\n\n // Report FPS\n const now = Date.now();\n if (now - this.lastFPSReport >= 1000) {\n const fps = this.frameCount / ((now - this.lastFPSReport) / 1000);\n logger.info(`[OCRWorker] OCR FPS: ${fps.toFixed(2)}`);\n\n parentPort?.postMessage({\n type: 'fps',\n fps,\n frameCount: this.frameCount,\n });\n\n this.frameCount = 0;\n this.lastFPSReport = now;\n }\n } else {\n // No new frame, brief yield\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (error) {\n logger.error('[OCRWorker] Processing error:', error);\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n }\n\n private async processFrame(): Promise<void> {\n // Read metadata atomically\n const metadata: SharedMetadata = {\n frameId: Atomics.load(this.atomicState, this.FRAME_ID_INDEX),\n width: Atomics.load(this.atomicState, this.WIDTH_INDEX),\n height: Atomics.load(this.atomicState, this.HEIGHT_INDEX),\n displayIndex: Atomics.load(this.atomicState, this.DISPLAY_INDEX),\n timestamp: Atomics.load(this.atomicState, this.TIMESTAMP_INDEX),\n };\n\n const results: OCRResult[] = [];\n\n if (this.config.processFullScreen) {\n // Process entire screen\n try {\n const screenBuffer = await this.extractFullScreenBuffer(metadata);\n const ocrResult = await this.ocrService.extractText(screenBuffer);\n results.push(ocrResult);\n\n logger.debug(`[OCRWorker] Full screen OCR: ${ocrResult.fullText.length} chars`);\n } catch (error) {\n logger.error('[OCRWorker] Full screen OCR failed:', error);\n }\n }\n\n // Process specific text regions if defined\n if (this.config.textRegions && this.config.textRegions.length > 0) {\n for (const region of this.config.textRegions) {\n try {\n const regionBuffer = await this.extractRegionBuffer(region, metadata);\n const ocrResult = await this.ocrService.extractText(regionBuffer);\n results.push(ocrResult);\n\n logger.debug(\n `[OCRWorker] Region OCR (${region.x},${region.y}): ${ocrResult.fullText.length} chars`\n );\n } catch (error) {\n logger.error('[OCRWorker] Region OCR failed:', error);\n }\n }\n }\n\n // Write combined results to buffer\n await this.writeResultsToBuffer(results, metadata.frameId);\n\n // Notify main thread\n const totalText = results.map((r) => r.fullText).join('\\n');\n const totalBlocks = results.reduce((sum, r) => sum + r.blocks.length, 0);\n\n parentPort?.postMessage({\n type: 'ocr_complete',\n frameId: metadata.frameId,\n displayIndex: metadata.displayIndex,\n textLength: totalText.length,\n blockCount: totalBlocks,\n hasText: totalText.length > 0,\n });\n }\n\n private async extractFullScreenBuffer(metadata: SharedMetadata): Promise<Buffer> {\n const bytesPerPixel = 4; // RGBA\n const totalPixels = metadata.width * metadata.height;\n const totalBytes = totalPixels * bytesPerPixel;\n\n // Create buffer for full screen\n const screenData = Buffer.allocUnsafe(totalBytes);\n\n // Copy all data\n for (let i = 0; i < totalBytes; i++) {\n screenData[i] = this.dataView.getUint8(this.DATA_OFFSET + i);\n }\n\n // Convert to PNG for OCR\n const pngBuffer = await sharp(screenData, {\n raw: {\n width: metadata.width,\n height: metadata.height,\n channels: 4,\n },\n })\n .png()\n .toBuffer();\n\n return pngBuffer;\n }\n\n private async extractRegionBuffer(\n region: { x: number; y: number; width: number; height: number },\n metadata: SharedMetadata\n ): Promise<Buffer> {\n const bytesPerPixel = 4; // RGBA\n const rowStride = metadata.width * bytesPerPixel;\n\n // Clamp region to screen bounds\n const x = Math.max(0, Math.min(region.x, metadata.width - 1));\n const y = Math.max(0, Math.min(region.y, metadata.height - 1));\n const width = Math.min(region.width, metadata.width - x);\n const height = Math.min(region.height, metadata.height - y);\n\n // Create buffer for region\n const regionData = Buffer.allocUnsafe(width * height * bytesPerPixel);\n\n // Copy region data row by row\n for (let row = 0; row < height; row++) {\n const sourceY = y + row;\n const sourceOffset = this.DATA_OFFSET + sourceY * rowStride + x * bytesPerPixel;\n const destOffset = row * width * bytesPerPixel;\n\n for (let i = 0; i < width * bytesPerPixel; i++) {\n regionData[destOffset + i] = this.dataView.getUint8(sourceOffset + i);\n }\n }\n\n // Convert to PNG for OCR\n const pngBuffer = await sharp(regionData, {\n raw: {\n width,\n height,\n channels: 4,\n },\n })\n .png()\n .toBuffer();\n\n return pngBuffer;\n }\n\n private async writeResultsToBuffer(results: OCRResult[], frameId: number): Promise<void> {\n // Combine all results\n const combinedResult = {\n frameId,\n timestamp: Date.now(),\n fullText: results.map((r) => r.fullText).join('\\n'),\n blocks: results.flatMap((r) => r.blocks),\n regions: results.length,\n };\n\n const resultJson = JSON.stringify(combinedResult);\n const resultBytes = Buffer.from(resultJson, 'utf-8');\n\n // Write to results buffer\n const offset = this.RESULTS_HEADER_SIZE;\n\n // Write length\n this.resultsView.setUint32(offset, resultBytes.length, true);\n\n // Write frame ID\n this.resultsView.setUint32(offset + 4, frameId, true);\n\n // Write timestamp\n this.resultsView.setFloat64(offset + 8, Date.now(), true);\n\n // Write text data\n const dataOffset = offset + 16;\n for (let i = 0; i < Math.min(resultBytes.length, this.MAX_TEXT_LENGTH); i++) {\n this.resultsView.setUint8(dataOffset + i, resultBytes[i]);\n }\n }\n\n stop(): void {\n this.isRunning = false;\n }\n\n async dispose(): Promise<void> {\n await this.ocrService.dispose();\n }\n\n updateTextRegions(regions: Array<{ x: number; y: number; width: number; height: number }>): void {\n this.config.textRegions = regions;\n }\n}\n\n// Worker entry point\nif (parentPort) {\n const { config, sharedBuffer, resultsBuffer } = workerData;\n const worker = new OCRWorker(config, sharedBuffer, resultsBuffer);\n\n // Handle messages from main thread\n parentPort.on('message', (msg) => {\n if (msg.type === 'stop') {\n worker.stop();\n worker.dispose().then(() => {\n parentPort?.postMessage({ type: 'stopped' });\n });\n } else if (msg.type === 'update_regions') {\n worker.updateTextRegions(msg.regions);\n }\n });\n\n // Run the worker\n worker.run().catch((error) => {\n logger.error('[OCRWorker] Fatal error:', error);\n parentPort?.postMessage({ type: 'error', error: error.message });\n process.exit(1);\n });\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};