feat(automation): add OS-level screen automation foundation services

This commit is contained in:
2025-11-22 14:09:39 +01:00
parent 81b2fd3cd3
commit 265b070606
10 changed files with 2031 additions and 9 deletions

View File

@@ -0,0 +1,96 @@
import type { ScreenRegion } from './ScreenRegion';
/**
* Represents an image template used for visual element detection.
* Templates are reference images that are matched against screen captures
* to locate UI elements without relying on CSS selectors or DOM access.
*/
export interface ImageTemplate {
/** Unique identifier for the template */
id: string;
/** Path to the template image file (relative to resources directory) */
imagePath: string;
/** Confidence threshold for matching (0.0-1.0, higher = more strict) */
confidence: number;
/** Optional region to limit search area for better performance */
searchRegion?: ScreenRegion;
/** Human-readable description of what this template represents */
description: string;
}
/**
* Template categories for organization and filtering.
*/
export type TemplateCategory =
| 'login'
| 'navigation'
| 'wizard'
| 'button'
| 'field'
| 'modal'
| 'indicator';
/**
* Extended template with category metadata.
*/
export interface CategorizedTemplate extends ImageTemplate {
category: TemplateCategory;
stepId?: number;
}
/**
* Create an ImageTemplate with default confidence.
*/
export function createImageTemplate(
id: string,
imagePath: string,
description: string,
options?: {
confidence?: number;
searchRegion?: ScreenRegion;
}
): ImageTemplate {
return {
id,
imagePath,
description,
confidence: options?.confidence ?? 0.9,
searchRegion: options?.searchRegion,
};
}
/**
* Validate that an ImageTemplate has all required fields.
*/
export function isValidTemplate(template: unknown): template is ImageTemplate {
if (typeof template !== 'object' || template === null) {
return false;
}
const t = template as Record<string, unknown>;
return (
typeof t.id === 'string' &&
t.id.length > 0 &&
typeof t.imagePath === 'string' &&
t.imagePath.length > 0 &&
typeof t.confidence === 'number' &&
t.confidence >= 0 &&
t.confidence <= 1 &&
typeof t.description === 'string'
);
}
/**
* Default confidence thresholds for different template types.
*/
export const DEFAULT_CONFIDENCE = {
/** High confidence for exact matches (buttons, icons) */
HIGH: 0.95,
/** Standard confidence for most UI elements */
STANDARD: 0.9,
/** Lower confidence for variable elements (text fields with content) */
LOW: 0.8,
/** Minimum acceptable confidence */
MINIMUM: 0.7,
} as const;

View File

@@ -0,0 +1,86 @@
/**
* Represents a rectangular region on the screen.
* Used for targeted screen capture and element location.
*/
export interface ScreenRegion {
x: number;
y: number;
width: number;
height: number;
}
/**
* Represents a point on the screen with x,y coordinates.
*/
export interface Point {
x: number;
y: number;
}
/**
* Represents the location of a detected UI element on screen.
* Contains the center point, bounding box, and confidence score.
*/
export interface ElementLocation {
center: Point;
bounds: ScreenRegion;
confidence: number;
}
/**
* Result of login state detection via screen recognition.
*/
export interface LoginDetectionResult {
isLoggedIn: boolean;
confidence: number;
detectedIndicators: string[];
error?: string;
}
/**
* Create a ScreenRegion from coordinates.
*/
export function createScreenRegion(x: number, y: number, width: number, height: number): ScreenRegion {
return { x, y, width, height };
}
/**
* Create a Point from coordinates.
*/
export function createPoint(x: number, y: number): Point {
return { x, y };
}
/**
* Calculate the center point of a ScreenRegion.
*/
export function getRegionCenter(region: ScreenRegion): Point {
return {
x: region.x + Math.floor(region.width / 2),
y: region.y + Math.floor(region.height / 2),
};
}
/**
* Check if a point is within a screen region.
*/
export function isPointInRegion(point: Point, region: ScreenRegion): boolean {
return (
point.x >= region.x &&
point.x <= region.x + region.width &&
point.y >= region.y &&
point.y <= region.y + region.height
);
}
/**
* Check if two screen regions overlap.
*/
export function regionsOverlap(a: ScreenRegion, b: ScreenRegion): boolean {
return !(
a.x + a.width < b.x ||
b.x + b.width < a.x ||
a.y + a.height < b.y ||
b.y + b.height < a.y
);
}