copilot-liku-cli 0.0.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.
- package/ARCHITECTURE.md +411 -0
- package/CONFIGURATION.md +302 -0
- package/CONTRIBUTING.md +225 -0
- package/ELECTRON_README.md +121 -0
- package/INSTALLATION.md +350 -0
- package/LICENSE.md +1 -0
- package/PROJECT_STATUS.md +229 -0
- package/QUICKSTART.md +255 -0
- package/README.md +167 -0
- package/TESTING.md +274 -0
- package/package.json +61 -0
- package/scripts/start.js +30 -0
- package/src/assets/tray-icon.png +0 -0
- package/src/cli/commands/agent.js +327 -0
- package/src/cli/commands/click.js +108 -0
- package/src/cli/commands/drag.js +85 -0
- package/src/cli/commands/find.js +109 -0
- package/src/cli/commands/keys.js +132 -0
- package/src/cli/commands/mouse.js +79 -0
- package/src/cli/commands/repl.js +290 -0
- package/src/cli/commands/screenshot.js +72 -0
- package/src/cli/commands/scroll.js +74 -0
- package/src/cli/commands/start.js +67 -0
- package/src/cli/commands/type.js +57 -0
- package/src/cli/commands/wait.js +84 -0
- package/src/cli/commands/window.js +104 -0
- package/src/cli/liku.js +249 -0
- package/src/cli/util/output.js +174 -0
- package/src/main/agents/base-agent.js +410 -0
- package/src/main/agents/builder.js +484 -0
- package/src/main/agents/index.js +62 -0
- package/src/main/agents/orchestrator.js +362 -0
- package/src/main/agents/researcher.js +511 -0
- package/src/main/agents/state-manager.js +344 -0
- package/src/main/agents/supervisor.js +365 -0
- package/src/main/agents/verifier.js +452 -0
- package/src/main/ai-service.js +1633 -0
- package/src/main/index.js +2208 -0
- package/src/main/inspect-service.js +467 -0
- package/src/main/system-automation.js +1186 -0
- package/src/main/ui-automation/config.js +76 -0
- package/src/main/ui-automation/core/helpers.js +41 -0
- package/src/main/ui-automation/core/index.js +15 -0
- package/src/main/ui-automation/core/powershell.js +82 -0
- package/src/main/ui-automation/elements/finder.js +274 -0
- package/src/main/ui-automation/elements/index.js +14 -0
- package/src/main/ui-automation/elements/wait.js +66 -0
- package/src/main/ui-automation/index.js +164 -0
- package/src/main/ui-automation/interactions/element-click.js +211 -0
- package/src/main/ui-automation/interactions/high-level.js +230 -0
- package/src/main/ui-automation/interactions/index.js +47 -0
- package/src/main/ui-automation/keyboard/index.js +15 -0
- package/src/main/ui-automation/keyboard/input.js +179 -0
- package/src/main/ui-automation/mouse/click.js +186 -0
- package/src/main/ui-automation/mouse/drag.js +88 -0
- package/src/main/ui-automation/mouse/index.js +30 -0
- package/src/main/ui-automation/mouse/movement.js +51 -0
- package/src/main/ui-automation/mouse/scroll.js +116 -0
- package/src/main/ui-automation/screenshot.js +183 -0
- package/src/main/ui-automation/window/index.js +23 -0
- package/src/main/ui-automation/window/manager.js +305 -0
- package/src/main/utils/time.js +62 -0
- package/src/main/visual-awareness.js +597 -0
- package/src/renderer/chat/chat.js +671 -0
- package/src/renderer/chat/index.html +725 -0
- package/src/renderer/chat/preload.js +112 -0
- package/src/renderer/overlay/index.html +648 -0
- package/src/renderer/overlay/overlay.js +782 -0
- package/src/renderer/overlay/preload.js +90 -0
- package/src/shared/grid-math.js +82 -0
- package/src/shared/inspect-types.js +230 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const { contextBridge, ipcRenderer } = require('electron');
|
|
2
|
+
|
|
3
|
+
// BLOCKER-1 FIX: Inline grid-math since sandboxed preload can't access 'path' module
|
|
4
|
+
// or require external files. These are small utilities needed for coordinate conversion.
|
|
5
|
+
|
|
6
|
+
const COARSE_SPACING = 100;
|
|
7
|
+
const FINE_SPACING = 25;
|
|
8
|
+
const START_OFFSET = COARSE_SPACING / 2;
|
|
9
|
+
const FINE_START = FINE_SPACING / 2;
|
|
10
|
+
|
|
11
|
+
function colLettersToIndex(letters) {
|
|
12
|
+
if (!letters || letters.length === 0) return null;
|
|
13
|
+
if (letters.length === 1) {
|
|
14
|
+
return letters.charCodeAt(0) - 65;
|
|
15
|
+
}
|
|
16
|
+
const first = letters.charCodeAt(0) - 65 + 1;
|
|
17
|
+
const second = letters.charCodeAt(1) - 65;
|
|
18
|
+
return (first * 26) + second;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function labelToScreenCoordinates(label) {
|
|
22
|
+
if (!label) return null;
|
|
23
|
+
const match = label.match(/^([A-Z]+)(\d+)(\.(\d)(\d))?$/);
|
|
24
|
+
if (!match) return null;
|
|
25
|
+
|
|
26
|
+
const [, letters, rowStr, , subColStr, subRowStr] = match;
|
|
27
|
+
const colIndex = colLettersToIndex(letters);
|
|
28
|
+
const rowIndex = parseInt(rowStr, 10);
|
|
29
|
+
if (colIndex === null || Number.isNaN(rowIndex)) return null;
|
|
30
|
+
|
|
31
|
+
if (subColStr && subRowStr) {
|
|
32
|
+
const subCol = parseInt(subColStr, 10);
|
|
33
|
+
const subRow = parseInt(subRowStr, 10);
|
|
34
|
+
if (Number.isNaN(subCol) || Number.isNaN(subRow)) return null;
|
|
35
|
+
const fineCol = (colIndex * 4) + subCol;
|
|
36
|
+
const fineRow = (rowIndex * 4) + subRow;
|
|
37
|
+
const x = FINE_START + fineCol * FINE_SPACING;
|
|
38
|
+
const y = FINE_START + fineRow * FINE_SPACING;
|
|
39
|
+
return { x, y, screenX: x, screenY: y, colIndex, rowIndex, fineCol, fineRow, subCol, subRow, isFine: true };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const x = START_OFFSET + colIndex * COARSE_SPACING;
|
|
43
|
+
const y = START_OFFSET + rowIndex * COARSE_SPACING;
|
|
44
|
+
return { x, y, screenX: x, screenY: y, colIndex, rowIndex, isFine: false };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const gridConstants = {
|
|
48
|
+
coarseSpacing: COARSE_SPACING,
|
|
49
|
+
fineSpacing: FINE_SPACING,
|
|
50
|
+
startOffset: START_OFFSET,
|
|
51
|
+
fineStart: FINE_START,
|
|
52
|
+
localFineRadius: 3
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Expose protected methods that allow the renderer process to use
|
|
56
|
+
// the ipcRenderer without exposing the entire object
|
|
57
|
+
contextBridge.exposeInMainWorld('electronAPI', {
|
|
58
|
+
// Send dot selection to main process
|
|
59
|
+
selectDot: (data) => ipcRenderer.send('dot-selected', data),
|
|
60
|
+
|
|
61
|
+
// Listen for mode changes
|
|
62
|
+
onModeChanged: (callback) => ipcRenderer.on('mode-changed', (event, mode) => callback(mode)),
|
|
63
|
+
|
|
64
|
+
// Listen for overlay commands (keyboard shortcuts routed via main process)
|
|
65
|
+
onOverlayCommand: (callback) => ipcRenderer.on('overlay-command', (event, data) => callback(data)),
|
|
66
|
+
|
|
67
|
+
// Get current state
|
|
68
|
+
getState: () => ipcRenderer.invoke('get-state'),
|
|
69
|
+
|
|
70
|
+
// Grid math helpers (inlined above)
|
|
71
|
+
getGridConstants: () => gridConstants,
|
|
72
|
+
labelToScreenCoordinates: (label) => labelToScreenCoordinates(label),
|
|
73
|
+
|
|
74
|
+
// ===== INSPECT MODE API =====
|
|
75
|
+
|
|
76
|
+
// Select an inspect region (sends targetId + bounds to main)
|
|
77
|
+
selectInspectRegion: (data) => ipcRenderer.send('inspect-region-selected', data),
|
|
78
|
+
|
|
79
|
+
// Listen for inspect regions updates
|
|
80
|
+
onInspectRegionsUpdate: (callback) => ipcRenderer.on('inspect-regions-update', (event, regions) => callback(regions)),
|
|
81
|
+
|
|
82
|
+
// Listen for inspect mode toggle
|
|
83
|
+
onInspectModeChanged: (callback) => ipcRenderer.on('inspect-mode-changed', (event, enabled) => callback(enabled)),
|
|
84
|
+
|
|
85
|
+
// Request inspect region detection
|
|
86
|
+
requestInspectRegions: () => ipcRenderer.send('request-inspect-regions'),
|
|
87
|
+
|
|
88
|
+
// Toggle inspect mode
|
|
89
|
+
toggleInspectMode: () => ipcRenderer.send('toggle-inspect-mode')
|
|
90
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const COARSE_SPACING = 100;
|
|
2
|
+
const FINE_SPACING = 25;
|
|
3
|
+
const START_OFFSET = COARSE_SPACING / 2;
|
|
4
|
+
const FINE_START = FINE_SPACING / 2;
|
|
5
|
+
|
|
6
|
+
function getColLetter(colIndex) {
|
|
7
|
+
let letter = '';
|
|
8
|
+
if (colIndex >= 26) {
|
|
9
|
+
letter += String.fromCharCode(65 + Math.floor(colIndex / 26) - 1);
|
|
10
|
+
}
|
|
11
|
+
letter += String.fromCharCode(65 + (colIndex % 26));
|
|
12
|
+
return letter;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function colLettersToIndex(letters) {
|
|
16
|
+
if (!letters || letters.length === 0) return null;
|
|
17
|
+
if (letters.length === 1) {
|
|
18
|
+
return letters.charCodeAt(0) - 65;
|
|
19
|
+
}
|
|
20
|
+
const first = letters.charCodeAt(0) - 65 + 1;
|
|
21
|
+
const second = letters.charCodeAt(1) - 65;
|
|
22
|
+
return (first * 26) + second;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function labelToScreenCoordinates(label) {
|
|
26
|
+
if (!label) return null;
|
|
27
|
+
const match = label.match(/^([A-Z]+)(\d+)(\.(\d)(\d))?$/);
|
|
28
|
+
if (!match) return null;
|
|
29
|
+
|
|
30
|
+
const [, letters, rowStr, , subColStr, subRowStr] = match;
|
|
31
|
+
const colIndex = colLettersToIndex(letters);
|
|
32
|
+
const rowIndex = parseInt(rowStr, 10);
|
|
33
|
+
if (colIndex === null || Number.isNaN(rowIndex)) return null;
|
|
34
|
+
|
|
35
|
+
if (subColStr && subRowStr) {
|
|
36
|
+
const subCol = parseInt(subColStr, 10);
|
|
37
|
+
const subRow = parseInt(subRowStr, 10);
|
|
38
|
+
if (Number.isNaN(subCol) || Number.isNaN(subRow)) return null;
|
|
39
|
+
const fineCol = (colIndex * 4) + subCol;
|
|
40
|
+
const fineRow = (rowIndex * 4) + subRow;
|
|
41
|
+
const x = FINE_START + fineCol * FINE_SPACING;
|
|
42
|
+
const y = FINE_START + fineRow * FINE_SPACING;
|
|
43
|
+
return {
|
|
44
|
+
x,
|
|
45
|
+
y,
|
|
46
|
+
screenX: x,
|
|
47
|
+
screenY: y,
|
|
48
|
+
colIndex,
|
|
49
|
+
rowIndex,
|
|
50
|
+
fineCol,
|
|
51
|
+
fineRow,
|
|
52
|
+
subCol,
|
|
53
|
+
subRow,
|
|
54
|
+
isFine: true
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const x = START_OFFSET + colIndex * COARSE_SPACING;
|
|
59
|
+
const y = START_OFFSET + rowIndex * COARSE_SPACING;
|
|
60
|
+
return {
|
|
61
|
+
x,
|
|
62
|
+
y,
|
|
63
|
+
screenX: x,
|
|
64
|
+
screenY: y,
|
|
65
|
+
colIndex,
|
|
66
|
+
rowIndex,
|
|
67
|
+
isFine: false
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
constants: {
|
|
73
|
+
coarseSpacing: COARSE_SPACING,
|
|
74
|
+
fineSpacing: FINE_SPACING,
|
|
75
|
+
startOffset: START_OFFSET,
|
|
76
|
+
fineStart: FINE_START,
|
|
77
|
+
localFineRadius: 3
|
|
78
|
+
},
|
|
79
|
+
getColLetter,
|
|
80
|
+
colLettersToIndex,
|
|
81
|
+
labelToScreenCoordinates
|
|
82
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspect Overlay Data Contracts
|
|
3
|
+
* Shared type definitions for inspect regions, window context, and action traces
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inspect Region Data Contract
|
|
8
|
+
* Represents an actionable region on screen detected through various sources
|
|
9
|
+
* @typedef {Object} InspectRegion
|
|
10
|
+
* @property {string} id - Unique identifier for the region
|
|
11
|
+
* @property {Object} bounds - Bounding box {x, y, width, height}
|
|
12
|
+
* @property {string} label - Human-readable label (e.g., "Search button")
|
|
13
|
+
* @property {string} text - Text content if available
|
|
14
|
+
* @property {string} role - Accessibility role (button, textbox, etc.)
|
|
15
|
+
* @property {number} confidence - Detection confidence 0-1
|
|
16
|
+
* @property {string} source - Detection source (accessibility, ocr, heuristic)
|
|
17
|
+
* @property {number} timestamp - When this region was detected
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Window Context Data Contract
|
|
22
|
+
* Information about the active window and process
|
|
23
|
+
* @typedef {Object} WindowContext
|
|
24
|
+
* @property {string} appName - Application name
|
|
25
|
+
* @property {string} windowTitle - Window title
|
|
26
|
+
* @property {number} pid - Process ID
|
|
27
|
+
* @property {Object} bounds - Window bounds {x, y, width, height}
|
|
28
|
+
* @property {number} zOrder - Z-order (depth) of window
|
|
29
|
+
* @property {number} scaleFactor - Display scale factor for DPI normalization
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Action Trace Data Contract
|
|
34
|
+
* Records of actions for replay and debugging
|
|
35
|
+
* @typedef {Object} ActionTrace
|
|
36
|
+
* @property {string} actionId - Unique action identifier
|
|
37
|
+
* @property {string} type - Action type (click, type, key, etc.)
|
|
38
|
+
* @property {string} [targetId] - ID of target region if applicable
|
|
39
|
+
* @property {number} x - X coordinate
|
|
40
|
+
* @property {number} y - Y coordinate
|
|
41
|
+
* @property {number} timestamp - When action was executed
|
|
42
|
+
* @property {string} outcome - Result (success, failed, pending)
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a new inspect region object
|
|
47
|
+
* @param {Object} params - Region parameters
|
|
48
|
+
* @returns {InspectRegion}
|
|
49
|
+
*/
|
|
50
|
+
function createInspectRegion(params) {
|
|
51
|
+
return {
|
|
52
|
+
id: params.id || `region-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
|
|
53
|
+
bounds: {
|
|
54
|
+
x: params.x || params.bounds?.x || 0,
|
|
55
|
+
y: params.y || params.bounds?.y || 0,
|
|
56
|
+
width: params.width || params.bounds?.width || 0,
|
|
57
|
+
height: params.height || params.bounds?.height || 0
|
|
58
|
+
},
|
|
59
|
+
label: params.label || params.name || '',
|
|
60
|
+
text: params.text || '',
|
|
61
|
+
role: params.role || params.controlType || 'unknown',
|
|
62
|
+
confidence: typeof params.confidence === 'number' ? params.confidence : 0.5,
|
|
63
|
+
source: params.source || 'unknown',
|
|
64
|
+
timestamp: params.timestamp || Date.now()
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create a new window context object
|
|
70
|
+
* @param {Object} params - Window parameters
|
|
71
|
+
* @returns {WindowContext}
|
|
72
|
+
*/
|
|
73
|
+
function createWindowContext(params) {
|
|
74
|
+
return {
|
|
75
|
+
appName: params.appName || params.processName || '',
|
|
76
|
+
windowTitle: params.windowTitle || params.title || '',
|
|
77
|
+
pid: params.pid || params.processId || 0,
|
|
78
|
+
bounds: {
|
|
79
|
+
x: params.bounds?.x || params.bounds?.X || 0,
|
|
80
|
+
y: params.bounds?.y || params.bounds?.Y || 0,
|
|
81
|
+
width: params.bounds?.width || params.bounds?.Width || 0,
|
|
82
|
+
height: params.bounds?.height || params.bounds?.Height || 0
|
|
83
|
+
},
|
|
84
|
+
zOrder: params.zOrder || 0,
|
|
85
|
+
scaleFactor: params.scaleFactor || 1
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a new action trace object
|
|
91
|
+
* @param {Object} params - Action parameters
|
|
92
|
+
* @returns {ActionTrace}
|
|
93
|
+
*/
|
|
94
|
+
function createActionTrace(params) {
|
|
95
|
+
return {
|
|
96
|
+
actionId: params.actionId || `action-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
|
|
97
|
+
type: params.type || 'unknown',
|
|
98
|
+
targetId: params.targetId || null,
|
|
99
|
+
x: params.x || 0,
|
|
100
|
+
y: params.y || 0,
|
|
101
|
+
timestamp: params.timestamp || Date.now(),
|
|
102
|
+
outcome: params.outcome || 'pending'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Normalize coordinates with scale factor
|
|
108
|
+
* @param {number} x - X coordinate
|
|
109
|
+
* @param {number} y - Y coordinate
|
|
110
|
+
* @param {number} scaleFactor - Display scale factor
|
|
111
|
+
* @returns {Object} Normalized {x, y}
|
|
112
|
+
*/
|
|
113
|
+
function normalizeCoordinates(x, y, scaleFactor = 1) {
|
|
114
|
+
return {
|
|
115
|
+
x: Math.round(x * scaleFactor),
|
|
116
|
+
y: Math.round(y * scaleFactor)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Denormalize coordinates from scaled to logical
|
|
122
|
+
* @param {number} x - X coordinate (scaled)
|
|
123
|
+
* @param {number} y - Y coordinate (scaled)
|
|
124
|
+
* @param {number} scaleFactor - Display scale factor
|
|
125
|
+
* @returns {Object} Logical {x, y}
|
|
126
|
+
*/
|
|
127
|
+
function denormalizeCoordinates(x, y, scaleFactor = 1) {
|
|
128
|
+
return {
|
|
129
|
+
x: Math.round(x / scaleFactor),
|
|
130
|
+
y: Math.round(y / scaleFactor)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check if a point is within a region's bounds
|
|
136
|
+
* @param {number} x - X coordinate
|
|
137
|
+
* @param {number} y - Y coordinate
|
|
138
|
+
* @param {InspectRegion} region - The region to check
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
function isPointInRegion(x, y, region) {
|
|
142
|
+
const { bounds } = region;
|
|
143
|
+
return x >= bounds.x &&
|
|
144
|
+
x < bounds.x + bounds.width &&
|
|
145
|
+
y >= bounds.y &&
|
|
146
|
+
y < bounds.y + bounds.height;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Find the closest region to a point
|
|
151
|
+
* @param {number} x - X coordinate
|
|
152
|
+
* @param {number} y - Y coordinate
|
|
153
|
+
* @param {InspectRegion[]} regions - Array of regions
|
|
154
|
+
* @returns {InspectRegion|null} Closest region or null
|
|
155
|
+
*/
|
|
156
|
+
function findClosestRegion(x, y, regions) {
|
|
157
|
+
if (!regions || regions.length === 0) return null;
|
|
158
|
+
|
|
159
|
+
let closest = null;
|
|
160
|
+
let minDist = Infinity;
|
|
161
|
+
|
|
162
|
+
for (const region of regions) {
|
|
163
|
+
const centerX = region.bounds.x + region.bounds.width / 2;
|
|
164
|
+
const centerY = region.bounds.y + region.bounds.height / 2;
|
|
165
|
+
const dist = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
|
|
166
|
+
|
|
167
|
+
if (dist < minDist) {
|
|
168
|
+
minDist = dist;
|
|
169
|
+
closest = region;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return closest;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Find region containing a point
|
|
178
|
+
* @param {number} x - X coordinate
|
|
179
|
+
* @param {number} y - Y coordinate
|
|
180
|
+
* @param {InspectRegion[]} regions - Array of regions
|
|
181
|
+
* @returns {InspectRegion|null} Containing region or null
|
|
182
|
+
*/
|
|
183
|
+
function findRegionAtPoint(x, y, regions) {
|
|
184
|
+
if (!regions || regions.length === 0) return null;
|
|
185
|
+
|
|
186
|
+
// Find all regions containing the point
|
|
187
|
+
const containing = regions.filter(r => isPointInRegion(x, y, r));
|
|
188
|
+
|
|
189
|
+
if (containing.length === 0) return null;
|
|
190
|
+
if (containing.length === 1) return containing[0];
|
|
191
|
+
|
|
192
|
+
// If multiple regions, return the smallest (most specific)
|
|
193
|
+
return containing.reduce((smallest, r) => {
|
|
194
|
+
const smallestArea = smallest.bounds.width * smallest.bounds.height;
|
|
195
|
+
const rArea = r.bounds.width * r.bounds.height;
|
|
196
|
+
return rArea < smallestArea ? r : smallest;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Format region for AI context
|
|
202
|
+
* @param {InspectRegion} region - The region to format
|
|
203
|
+
* @returns {Object} AI-friendly format
|
|
204
|
+
*/
|
|
205
|
+
function formatRegionForAI(region) {
|
|
206
|
+
return {
|
|
207
|
+
id: region.id,
|
|
208
|
+
label: region.label,
|
|
209
|
+
text: region.text,
|
|
210
|
+
role: region.role,
|
|
211
|
+
confidence: region.confidence,
|
|
212
|
+
center: {
|
|
213
|
+
x: Math.round(region.bounds.x + region.bounds.width / 2),
|
|
214
|
+
y: Math.round(region.bounds.y + region.bounds.height / 2)
|
|
215
|
+
},
|
|
216
|
+
bounds: region.bounds
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
createInspectRegion,
|
|
222
|
+
createWindowContext,
|
|
223
|
+
createActionTrace,
|
|
224
|
+
normalizeCoordinates,
|
|
225
|
+
denormalizeCoordinates,
|
|
226
|
+
isPointInRegion,
|
|
227
|
+
findClosestRegion,
|
|
228
|
+
findRegionAtPoint,
|
|
229
|
+
formatRegionForAI
|
|
230
|
+
};
|