windows-mcp 0.5.7__py3-none-any.whl → 0.5.9__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.
windows_mcp/tree/utils.py CHANGED
@@ -1,22 +1,22 @@
1
- import random
2
- from uiautomation import Control
3
-
4
- def random_point_within_bounding_box(node: Control, scale_factor: float = 1.0) -> tuple[int, int]:
5
- """
6
- Generate a random point within a scaled-down bounding box.
7
-
8
- Args:
9
- node (Control): The node with a bounding rectangle
10
- scale_factor (float, optional): The factor to scale down the bounding box. Defaults to 1.0.
11
-
12
- Returns:
13
- tuple: A random point (x, y) within the scaled-down bounding box
14
- """
15
- box = node.BoundingRectangle
16
- scaled_width = int(box.width() * scale_factor)
17
- scaled_height = int(box.height() * scale_factor)
18
- scaled_left = box.left + (box.width() - scaled_width) // 2
19
- scaled_top = box.top + (box.height() - scaled_height) // 2
20
- x = random.randint(scaled_left, scaled_left + scaled_width)
21
- y = random.randint(scaled_top, scaled_top + scaled_height)
1
+ import random
2
+ from windows_mcp.uia import Control
3
+
4
+ def random_point_within_bounding_box(node: Control, scale_factor: float = 1.0) -> tuple[int, int]:
5
+ """
6
+ Generate a random point within a scaled-down bounding box.
7
+
8
+ Args:
9
+ node (Control): The node with a bounding rectangle
10
+ scale_factor (float, optional): The factor to scale down the bounding box. Defaults to 1.0.
11
+
12
+ Returns:
13
+ tuple: A random point (x, y) within the scaled-down bounding box
14
+ """
15
+ box = node.BoundingRectangle
16
+ scaled_width = int(box.width() * scale_factor)
17
+ scaled_height = int(box.height() * scale_factor)
18
+ scaled_left = box.left + (box.width() - scaled_width) // 2
19
+ scaled_top = box.top + (box.height() - scaled_height) // 2
20
+ x = random.randint(scaled_left, scaled_left + scaled_width)
21
+ y = random.randint(scaled_top, scaled_top + scaled_height)
22
22
  return (x, y)
windows_mcp/tree/views.py CHANGED
@@ -2,37 +2,39 @@ from dataclasses import dataclass,field
2
2
  from tabulate import tabulate
3
3
  from typing import Optional
4
4
 
5
- @dataclass
6
- class DOMInfo:
7
- horizontal_scrollable: bool
8
- horizontal_scroll_percent: float
9
- vertical_scrollable: bool
10
- vertical_scroll_percent: float
11
-
12
5
  @dataclass
13
6
  class TreeState:
7
+ root_node:Optional['TreeElementNode']=None
8
+ dom_node:Optional['ScrollElementNode']=None
14
9
  interactive_nodes:list['TreeElementNode']=field(default_factory=list)
15
10
  scrollable_nodes:list['ScrollElementNode']=field(default_factory=list)
16
11
  dom_informative_nodes:list['TextElementNode']=field(default_factory=list)
17
- dom_info:Optional['DOMInfo']=None
18
12
 
19
13
  def interactive_elements_to_string(self) -> str:
20
14
  if not self.interactive_nodes:
21
15
  return "No interactive elements"
22
- headers = ["Label", "App Name", "ControlType", "Name", "Value", "Shortcut", "Coordinates" ,"IsFocused"]
23
- rows = [node.to_row(idx) for idx, node in enumerate(self.interactive_nodes)]
24
- return tabulate(rows, headers=headers, tablefmt="simple")
16
+ # TOON-like format: Pipe-separated values with clear header
17
+ # Using abbreviations in header to save tokens
18
+ header = "# id|app|type|name|coords|focus"
19
+ rows = [header]
20
+ for idx, node in enumerate(self.interactive_nodes):
21
+ row = f"{idx}|{node.app_name}|{node.control_type}|{node.name}|{node.center.to_string()}|{node.is_focused}"
22
+ rows.append(row)
23
+ return "\n".join(rows)
25
24
 
26
25
  def scrollable_elements_to_string(self) -> str:
27
26
  if not self.scrollable_nodes:
28
27
  return "No scrollable elements"
29
- headers = [
30
- "Label", "App Name", "ControlType", "Name", "Coordinates",
31
- "Horizontal Scrollable", "Horizontal Scroll Percent(%)", "Vertical Scrollable", "Vertical Scroll Percent(%)", "IsFocused"
32
- ]
28
+ # TOON-like format
29
+ header = "# id|app|type|name|coords|h_scroll|h_pct|v_scroll|v_pct|focus"
30
+ rows = [header]
33
31
  base_index = len(self.interactive_nodes)
34
- rows = [node.to_row(idx, base_index) for idx, node in enumerate(self.scrollable_nodes)]
35
- return tabulate(rows, headers=headers, tablefmt="simple")
32
+ for idx, node in enumerate(self.scrollable_nodes):
33
+ row = (f"{base_index + idx}|{node.app_name}|{node.control_type}|{node.name}|"
34
+ f"{node.center.to_string()}|{node.horizontal_scrollable}|{node.horizontal_scroll_percent}|"
35
+ f"{node.vertical_scrollable}|{node.vertical_scroll_percent}|{node.is_focused}")
36
+ rows.append(row)
37
+ return "\n".join(rows)
36
38
 
37
39
  @dataclass
38
40
  class BoundingBox:
@@ -43,6 +45,17 @@ class BoundingBox:
43
45
  width:int
44
46
  height:int
45
47
 
48
+ @classmethod
49
+ def from_bounding_rectangle(cls,bounding_rectangle:'BoundingRectangle')->'BoundingBox':
50
+ return cls(
51
+ left=bounding_rectangle.left,
52
+ top=bounding_rectangle.top,
53
+ right=bounding_rectangle.right,
54
+ bottom=bounding_rectangle.bottom,
55
+ width=bounding_rectangle.width(),
56
+ height=bounding_rectangle.height()
57
+ )
58
+
46
59
  def get_center(self)->'Center':
47
60
  return Center(x=self.left+self.width//2,y=self.top+self.height//2)
48
61
 
@@ -68,16 +81,28 @@ class Center:
68
81
 
69
82
  @dataclass
70
83
  class TreeElementNode:
71
- name: str
72
- control_type: str
73
- app_name: str
74
- value:str
75
- shortcut: str
76
84
  bounding_box: BoundingBox
77
85
  center: Center
78
- xpath:str
79
- is_focused:bool
86
+ name: str=''
87
+ control_type: str=''
88
+ app_name: str=''
89
+ value:str=''
90
+ shortcut: str=''
91
+ xpath:str=''
92
+ is_focused:bool=False
93
+
94
+ def update_from_node(self,node:'TreeElementNode'):
95
+ self.name=node.name
96
+ self.control_type=node.control_type
97
+ self.app_name=node.app_name
98
+ self.value=node.value
99
+ self.shortcut=node.shortcut
100
+ self.bounding_box=node.bounding_box
101
+ self.center=node.center
102
+ self.xpath=node.xpath
103
+ self.is_focused=node.is_focused
80
104
 
105
+ # Legacy method kept for compatibility if needed, but not used in new format
81
106
  def to_row(self, index: int):
82
107
  return [index, self.app_name, self.control_type, self.name, self.value, self.shortcut, self.center.to_string(),self.is_focused]
83
108
 
@@ -95,6 +120,7 @@ class ScrollElementNode:
95
120
  vertical_scroll_percent: float
96
121
  is_focused: bool
97
122
 
123
+ # Legacy method kept for compatibility
98
124
  def to_row(self, index: int, base_index: int):
99
125
  return [
100
126
  base_index + index,
@@ -113,4 +139,4 @@ class ScrollElementNode:
113
139
  class TextElementNode:
114
140
  text:str
115
141
 
116
- ElementNode=TreeElementNode|ScrollElementNode
142
+ ElementNode=TreeElementNode|ScrollElementNode|TextElementNode
@@ -0,0 +1,4 @@
1
+ from .enums import *
2
+ from .core import *
3
+ from .patterns import *
4
+ from .controls import *