oagi-core 0.14.0__py3-none-any.whl → 0.14.2__py3-none-any.whl

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.
@@ -13,9 +13,11 @@ from pydantic import BaseModel, Field
13
13
 
14
14
  from oagi.handler.screen_manager import Screen
15
15
 
16
+ from ..constants import DEFAULT_STEP_DELAY
16
17
  from ..exceptions import check_optional_dependency
17
18
  from ..types import Action, ActionType, parse_coords, parse_drag_coords, parse_scroll
18
19
  from .capslock_manager import CapsLockManager
20
+ from .utils import CoordinateScaler, normalize_key, parse_hotkey
19
21
 
20
22
  check_optional_dependency("pyautogui", "PyautoguiActionHandler", "desktop")
21
23
  import pyautogui # noqa: E402
@@ -57,6 +59,12 @@ class PyautoguiConfig(BaseModel):
57
59
  default=0.1,
58
60
  description="Delay in seconds after moving to position before clicking",
59
61
  )
62
+ post_batch_delay: float = Field(
63
+ default=DEFAULT_STEP_DELAY,
64
+ ge=0,
65
+ description="Delay after executing all actions in a batch (seconds). "
66
+ "Allows UI to settle before next screenshot.",
67
+ )
60
68
 
61
69
 
62
70
  class PyautoguiActionHandler:
@@ -85,6 +93,15 @@ class PyautoguiActionHandler:
85
93
  self.caps_manager = CapsLockManager(mode=self.config.capslock_mode)
86
94
  # The origin position of coordinates (the top-left corner of the target screen)
87
95
  self.origin_x, self.origin_y = 0, 0
96
+ # Initialize coordinate scaler (OAGI uses 0-1000 normalized coordinates)
97
+ self._coord_scaler = CoordinateScaler(
98
+ source_width=1000,
99
+ source_height=1000,
100
+ target_width=self.screen_width,
101
+ target_height=self.screen_height,
102
+ origin_x=self.origin_x,
103
+ origin_y=self.origin_y,
104
+ )
88
105
 
89
106
  def reset(self):
90
107
  """Reset handler state.
@@ -102,6 +119,9 @@ class PyautoguiActionHandler:
102
119
  """
103
120
  self.screen_width, self.screen_height = screen.width, screen.height
104
121
  self.origin_x, self.origin_y = screen.x, screen.y
122
+ # Update coordinate scaler
123
+ self._coord_scaler.set_target_size(screen.width, screen.height)
124
+ self._coord_scaler.set_origin(screen.x, screen.y)
105
125
 
106
126
  def _denormalize_coords(self, x: float, y: float) -> tuple[int, int]:
107
127
  """Convert coordinates from 0-1000 range to actual screen coordinates.
@@ -109,26 +129,7 @@ class PyautoguiActionHandler:
109
129
  Also handles corner coordinates to prevent PyAutoGUI fail-safe trigger.
110
130
  Corner coordinates (0,0), (0,max), (max,0), (max,max) are offset by 1 pixel.
111
131
  """
112
- screen_x = int(x * self.screen_width / 1000)
113
- screen_y = int(y * self.screen_height / 1000)
114
-
115
- # Prevent fail-safe by adjusting corner coordinates
116
- # Check if coordinates are at screen corners (with small tolerance)
117
- if screen_x < 1:
118
- screen_x = 1
119
- elif screen_x > self.screen_width - 1:
120
- screen_x = self.screen_width - 1
121
-
122
- if screen_y < 1:
123
- screen_y = 1
124
- elif screen_y > self.screen_height - 1:
125
- screen_y = self.screen_height - 1
126
-
127
- # Add origin offset to convert relative to top-left corner
128
- screen_x += self.origin_x
129
- screen_y += self.origin_y
130
-
131
- return screen_x, screen_y
132
+ return self._coord_scaler.scale(x, y, prevent_failsafe=True)
132
133
 
133
134
  def _parse_coords(self, args_str: str) -> tuple[int, int]:
134
135
  """Extract x, y coordinates from argument string."""
@@ -156,28 +157,15 @@ class PyautoguiActionHandler:
156
157
 
157
158
  def _normalize_key(self, key: str) -> str:
158
159
  """Normalize key names for consistency."""
159
- key = key.strip().lower()
160
- # Normalize caps lock variations
161
- hotkey_variations_mapping = {
162
- "capslock": ["caps_lock", "caps", "capslock"],
163
- "pgup": ["page_up", "pageup"],
164
- "pgdn": ["page_down", "pagedown"],
165
- }
166
- for normalized, variations in hotkey_variations_mapping.items():
167
- if key in variations:
168
- return normalized
169
- # Remap ctrl to command on macOS if enabled
170
- if self.config.macos_ctrl_to_cmd and sys.platform == "darwin" and key == "ctrl":
171
- return "command"
172
- return key
160
+ return normalize_key(key, macos_ctrl_to_cmd=self.config.macos_ctrl_to_cmd)
173
161
 
174
162
  def _parse_hotkey(self, args_str: str) -> list[str]:
175
163
  """Parse hotkey string into list of keys."""
176
- # Remove parentheses if present
177
- args_str = args_str.strip("()")
178
- # Split by '+' to get individual keys
179
- keys = [self._normalize_key(key) for key in args_str.split("+")]
180
- return keys
164
+ return parse_hotkey(
165
+ args_str,
166
+ macos_ctrl_to_cmd=self.config.macos_ctrl_to_cmd,
167
+ validate=False, # Don't validate, let pyautogui handle invalid keys
168
+ )
181
169
 
182
170
  def _move_and_wait(self, x: int, y: int) -> None:
183
171
  """Move cursor to position and wait before clicking."""
@@ -292,3 +280,7 @@ class PyautoguiActionHandler:
292
280
  except Exception as e:
293
281
  print(f"Error executing action {action.type}: {e}")
294
282
  raise
283
+
284
+ # Wait after batch for UI to settle before next screenshot
285
+ if self.config.post_batch_delay > 0:
286
+ time.sleep(self.config.post_batch_delay)