pygpt-net 2.7.4__py3-none-any.whl → 2.7.6__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.
- pygpt_net/CHANGELOG.txt +15 -0
- pygpt_net/__init__.py +4 -4
- pygpt_net/app_core.py +4 -2
- pygpt_net/controller/__init__.py +5 -1
- pygpt_net/controller/assistant/assistant.py +1 -4
- pygpt_net/controller/assistant/batch.py +5 -504
- pygpt_net/controller/assistant/editor.py +5 -5
- pygpt_net/controller/assistant/files.py +16 -16
- pygpt_net/controller/chat/handler/google_stream.py +307 -1
- pygpt_net/controller/chat/handler/worker.py +10 -25
- pygpt_net/controller/chat/handler/xai_stream.py +621 -52
- pygpt_net/controller/chat/image.py +2 -2
- pygpt_net/controller/debug/fixtures.py +3 -2
- pygpt_net/controller/dialogs/confirm.py +73 -101
- pygpt_net/controller/files/files.py +65 -4
- pygpt_net/controller/lang/mapping.py +9 -9
- pygpt_net/controller/painter/capture.py +50 -1
- pygpt_net/controller/presets/presets.py +2 -1
- pygpt_net/controller/remote_store/__init__.py +12 -0
- pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
- pygpt_net/controller/remote_store/google/batch.py +402 -0
- pygpt_net/controller/remote_store/google/store.py +615 -0
- pygpt_net/controller/remote_store/openai/__init__.py +12 -0
- pygpt_net/controller/remote_store/openai/batch.py +524 -0
- pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
- pygpt_net/controller/remote_store/remote_store.py +35 -0
- pygpt_net/controller/ui/ui.py +20 -1
- pygpt_net/core/assistants/assistants.py +3 -15
- pygpt_net/core/db/database.py +5 -3
- pygpt_net/core/filesystem/url.py +4 -1
- pygpt_net/core/locale/placeholder.py +35 -0
- pygpt_net/core/remote_store/__init__.py +12 -0
- pygpt_net/core/remote_store/google/__init__.py +11 -0
- pygpt_net/core/remote_store/google/files.py +224 -0
- pygpt_net/core/remote_store/google/store.py +248 -0
- pygpt_net/core/remote_store/openai/__init__.py +11 -0
- pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
- pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
- pygpt_net/core/remote_store/remote_store.py +24 -0
- pygpt_net/core/render/web/body.py +3 -2
- pygpt_net/core/types/chunk.py +27 -0
- pygpt_net/data/config/config.json +8 -4
- pygpt_net/data/config/models.json +77 -3
- pygpt_net/data/config/settings.json +45 -0
- pygpt_net/data/js/app/template.js +1 -1
- pygpt_net/data/js/app.min.js +2 -2
- pygpt_net/data/locale/locale.de.ini +44 -41
- pygpt_net/data/locale/locale.en.ini +56 -43
- pygpt_net/data/locale/locale.es.ini +44 -41
- pygpt_net/data/locale/locale.fr.ini +44 -41
- pygpt_net/data/locale/locale.it.ini +44 -41
- pygpt_net/data/locale/locale.pl.ini +45 -42
- pygpt_net/data/locale/locale.uk.ini +44 -41
- pygpt_net/data/locale/locale.zh.ini +44 -41
- pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
- pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
- pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
- pygpt_net/item/assistant.py +1 -211
- pygpt_net/item/ctx.py +3 -3
- pygpt_net/item/store.py +238 -0
- pygpt_net/js_rc.py +2449 -2447
- pygpt_net/migrations/Version20260102190000.py +35 -0
- pygpt_net/migrations/__init__.py +3 -1
- pygpt_net/plugin/cmd_mouse_control/config.py +471 -1
- pygpt_net/plugin/cmd_mouse_control/plugin.py +487 -22
- pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
- pygpt_net/plugin/idx_llama_index/config.py +2 -2
- pygpt_net/provider/api/anthropic/__init__.py +10 -8
- pygpt_net/provider/api/google/__init__.py +21 -58
- pygpt_net/provider/api/google/chat.py +545 -129
- pygpt_net/provider/api/google/computer.py +190 -0
- pygpt_net/provider/api/google/realtime/realtime.py +2 -2
- pygpt_net/provider/api/google/remote_tools.py +93 -0
- pygpt_net/provider/api/google/store.py +546 -0
- pygpt_net/provider/api/google/worker/__init__.py +0 -0
- pygpt_net/provider/api/google/worker/importer.py +392 -0
- pygpt_net/provider/api/openai/__init__.py +7 -3
- pygpt_net/provider/api/openai/computer.py +10 -1
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/provider/api/openai/store.py +6 -6
- pygpt_net/provider/api/openai/worker/importer.py +24 -24
- pygpt_net/provider/api/x_ai/__init__.py +10 -9
- pygpt_net/provider/api/x_ai/chat.py +272 -102
- pygpt_net/provider/core/config/patch.py +16 -1
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
- pygpt_net/provider/core/model/patch.py +17 -3
- pygpt_net/provider/core/preset/json_file.py +13 -7
- pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
- pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
- pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
- pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
- pygpt_net/provider/llms/google.py +2 -2
- pygpt_net/tools/image_viewer/ui/dialogs.py +298 -12
- pygpt_net/tools/text_editor/ui/widgets.py +5 -1
- pygpt_net/ui/base/config_dialog.py +3 -2
- pygpt_net/ui/base/context_menu.py +44 -1
- pygpt_net/ui/dialog/assistant.py +3 -3
- pygpt_net/ui/dialog/plugins.py +3 -1
- pygpt_net/ui/dialog/remote_store_google.py +539 -0
- pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
- pygpt_net/ui/dialogs.py +5 -3
- pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
- pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
- pygpt_net/ui/layout/toolbox/indexes.py +22 -19
- pygpt_net/ui/layout/toolbox/model.py +28 -5
- pygpt_net/ui/menu/tools.py +13 -5
- pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
- pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
- pygpt_net/ui/widget/element/button.py +4 -4
- pygpt_net/ui/widget/image/display.py +25 -8
- pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
- pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
- pygpt_net/ui/widget/option/checkbox_list.py +47 -9
- pygpt_net/ui/widget/option/combo.py +39 -3
- pygpt_net/ui/widget/tabs/output.py +9 -1
- pygpt_net/ui/widget/textarea/editor.py +14 -1
- pygpt_net/ui/widget/textarea/input.py +20 -7
- pygpt_net/ui/widget/textarea/notepad.py +24 -1
- pygpt_net/ui/widget/textarea/output.py +23 -1
- pygpt_net/ui/widget/textarea/web.py +16 -1
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/METADATA +41 -2
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/RECORD +158 -132
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/entry_points.txt +0 -0
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
# Website: https://pygpt.net #
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
|
-
# Created By : Marcin
|
|
9
|
-
# Updated Date:
|
|
8
|
+
# Created By : Marcin Szczyglinski #
|
|
9
|
+
# Updated Date: 2026.01.02 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
+
import sys
|
|
12
13
|
import time
|
|
13
14
|
|
|
14
15
|
from pynput.mouse import Button, Controller as MouseController
|
|
@@ -22,6 +23,11 @@ class WorkerSignals(BaseSignals):
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class Worker(BaseWorker):
|
|
26
|
+
"""
|
|
27
|
+
Host worker: executes computer-use actions using native OS input (pynput).
|
|
28
|
+
It supports the full set of Computer Use commands. Each response includes "url" (empty on host).
|
|
29
|
+
"""
|
|
30
|
+
|
|
25
31
|
def __init__(self, *args, **kwargs):
|
|
26
32
|
super(Worker, self).__init__()
|
|
27
33
|
self.signals = WorkerSignals()
|
|
@@ -42,57 +48,117 @@ class Worker(BaseWorker):
|
|
|
42
48
|
break
|
|
43
49
|
response = None
|
|
44
50
|
try:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
51
|
+
cmd = item.get("cmd")
|
|
52
|
+
if not cmd:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
# alias before gating
|
|
56
|
+
if cmd == "screenshot":
|
|
57
|
+
item = dict(item)
|
|
58
|
+
item["cmd"] = "get_screenshot"
|
|
59
|
+
cmd = "get_screenshot"
|
|
60
|
+
|
|
61
|
+
# allow only plugin-declared commands
|
|
62
|
+
allowed = getattr(self.plugin, "allowed_cmds", None)
|
|
63
|
+
if isinstance(allowed, (list, set, tuple)) and cmd not in allowed:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# open web browser
|
|
67
|
+
if cmd == "open_web_browser":
|
|
68
|
+
response = self.cmd_open_web_browser(item)
|
|
69
|
+
|
|
70
|
+
# get mouse position
|
|
71
|
+
elif cmd == "get_mouse_position":
|
|
72
|
+
response = self.cmd_mouse_get_pos(item)
|
|
73
|
+
|
|
74
|
+
# set mouse position
|
|
75
|
+
elif cmd == "mouse_move":
|
|
76
|
+
if self.plugin.get_option_value("allow_mouse_move"):
|
|
77
|
+
response = self.cmd_mouse_move(item)
|
|
78
|
+
|
|
79
|
+
# drag mouse
|
|
80
|
+
elif cmd == "mouse_drag":
|
|
81
|
+
if self.plugin.get_option_value("allow_mouse_move"):
|
|
82
|
+
response = self.cmd_mouse_drag(item)
|
|
83
|
+
|
|
84
|
+
# mouse click
|
|
85
|
+
elif cmd == "mouse_click":
|
|
86
|
+
if self.plugin.get_option_value("allow_mouse_click"):
|
|
87
|
+
response = self.cmd_mouse_click(item)
|
|
88
|
+
|
|
89
|
+
# mouse scroll
|
|
90
|
+
elif cmd == "mouse_scroll":
|
|
91
|
+
if self.plugin.get_option_value("allow_mouse_scroll"):
|
|
92
|
+
response = self.cmd_mouse_scroll(item)
|
|
93
|
+
|
|
94
|
+
# screenshot
|
|
95
|
+
elif cmd == "get_screenshot":
|
|
96
|
+
if self.plugin.get_option_value("allow_screenshot"):
|
|
97
|
+
response = self.cmd_make_screenshot(item)
|
|
98
|
+
|
|
99
|
+
# keyboard key
|
|
100
|
+
elif cmd == "keyboard_key":
|
|
101
|
+
if self.plugin.get_option_value("allow_keyboard"):
|
|
102
|
+
response = self.cmd_keyboard_key(item)
|
|
103
|
+
|
|
104
|
+
# keyboard keys
|
|
105
|
+
elif cmd == "keyboard_keys":
|
|
106
|
+
if self.plugin.get_option_value("allow_keyboard"):
|
|
107
|
+
response = self.cmd_keyboard_keys(item)
|
|
108
|
+
|
|
109
|
+
# keyboard type
|
|
110
|
+
elif cmd == "keyboard_type":
|
|
111
|
+
if self.plugin.get_option_value("allow_keyboard"):
|
|
112
|
+
response = self.cmd_keyboard_type(item)
|
|
113
|
+
|
|
114
|
+
# wait
|
|
115
|
+
elif cmd == "wait":
|
|
116
|
+
response = self.cmd_wait(item)
|
|
117
|
+
|
|
118
|
+
# Computer Use: added commands (host-native)
|
|
119
|
+
elif cmd == "wait_5_seconds":
|
|
120
|
+
response = self.cmd_wait_5_seconds(item)
|
|
121
|
+
elif cmd == "go_back":
|
|
122
|
+
response = self.cmd_go_back(item)
|
|
123
|
+
elif cmd == "go_forward":
|
|
124
|
+
response = self.cmd_go_forward(item)
|
|
125
|
+
elif cmd == "search":
|
|
126
|
+
response = self.cmd_search(item)
|
|
127
|
+
elif cmd == "navigate":
|
|
128
|
+
response = self.cmd_navigate(item)
|
|
129
|
+
elif cmd == "click_at":
|
|
130
|
+
response = self.cmd_click_at(item)
|
|
131
|
+
elif cmd == "hover_at":
|
|
132
|
+
response = self.cmd_hover_at(item)
|
|
133
|
+
elif cmd == "type_text_at":
|
|
134
|
+
response = self.cmd_type_text_at(item)
|
|
135
|
+
elif cmd == "key_combination":
|
|
136
|
+
response = self.cmd_key_combination(item)
|
|
137
|
+
elif cmd == "scroll_document":
|
|
138
|
+
response = self.cmd_scroll_document(item)
|
|
139
|
+
elif cmd == "scroll_at":
|
|
140
|
+
response = self.cmd_scroll_at(item)
|
|
141
|
+
elif cmd == "drag_and_drop":
|
|
142
|
+
response = self.cmd_drag_and_drop(item)
|
|
143
|
+
|
|
144
|
+
# Action-style
|
|
145
|
+
elif cmd == "click":
|
|
146
|
+
response = self.cmd_click(item)
|
|
147
|
+
elif cmd == "double_click":
|
|
148
|
+
response = self.cmd_double_click(item)
|
|
149
|
+
elif cmd == "move":
|
|
150
|
+
response = self.cmd_move(item)
|
|
151
|
+
elif cmd == "type":
|
|
152
|
+
response = self.cmd_type_text(item)
|
|
153
|
+
elif cmd == "keypress":
|
|
154
|
+
response = self.cmd_keypress(item)
|
|
155
|
+
elif cmd == "scroll":
|
|
156
|
+
response = self.cmd_scroll(item)
|
|
157
|
+
elif cmd == "drag":
|
|
158
|
+
response = self.cmd_drag(item)
|
|
159
|
+
|
|
160
|
+
if response:
|
|
161
|
+
responses.append(response)
|
|
96
162
|
|
|
97
163
|
except Exception as e:
|
|
98
164
|
responses.append(
|
|
@@ -103,7 +169,7 @@ class Worker(BaseWorker):
|
|
|
103
169
|
)
|
|
104
170
|
|
|
105
171
|
if len(responses) > 0:
|
|
106
|
-
self.reply_more(responses)
|
|
172
|
+
self.reply_more(responses) # send response
|
|
107
173
|
|
|
108
174
|
except Exception as e:
|
|
109
175
|
self.error(e)
|
|
@@ -114,6 +180,62 @@ class Worker(BaseWorker):
|
|
|
114
180
|
"""Handle destroyed event."""
|
|
115
181
|
self.cleanup()
|
|
116
182
|
|
|
183
|
+
# ========================= Helpers ========================= #
|
|
184
|
+
|
|
185
|
+
def _get_screen_size(self) -> tuple:
|
|
186
|
+
screen = self.window.app.primaryScreen()
|
|
187
|
+
size = screen.size()
|
|
188
|
+
return size.width(), size.height()
|
|
189
|
+
|
|
190
|
+
def _denorm_x(self, x_norm: int) -> int:
|
|
191
|
+
w, _ = self._get_screen_size()
|
|
192
|
+
x_norm = max(0, min(999, int(x_norm)))
|
|
193
|
+
return int(round(x_norm / 1000.0 * w))
|
|
194
|
+
|
|
195
|
+
def _denorm_y(self, y_norm: int) -> int:
|
|
196
|
+
_, h = self._get_screen_size()
|
|
197
|
+
y_norm = max(0, min(999, int(y_norm)))
|
|
198
|
+
return int(round(y_norm / 1000.0 * h))
|
|
199
|
+
|
|
200
|
+
def _is_normalized_pair(self, x, y) -> bool:
|
|
201
|
+
try:
|
|
202
|
+
xi, yi = int(x), int(y)
|
|
203
|
+
return 0 <= xi <= 999 and 0 <= yi <= 999
|
|
204
|
+
except Exception:
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
def _is_mac(self) -> bool:
|
|
208
|
+
return sys.platform == "darwin"
|
|
209
|
+
|
|
210
|
+
# ========================= Legacy-compatible commands ========================= #
|
|
211
|
+
|
|
212
|
+
def cmd_open_web_browser(self, item: dict) -> dict:
|
|
213
|
+
"""
|
|
214
|
+
Open web browser
|
|
215
|
+
|
|
216
|
+
:param item: command item
|
|
217
|
+
:return: response item
|
|
218
|
+
"""
|
|
219
|
+
import webbrowser
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
self.msg = "Open web browser"
|
|
223
|
+
self.log(self.msg)
|
|
224
|
+
url = ""
|
|
225
|
+
if self.has_param(item, "url"):
|
|
226
|
+
url = self.get_param(item, "url")
|
|
227
|
+
webbrowser.open(url)
|
|
228
|
+
result = self.get_current(item)
|
|
229
|
+
self.log("Response: {}".format(result))
|
|
230
|
+
except Exception as e:
|
|
231
|
+
result = self.throw_error(e)
|
|
232
|
+
|
|
233
|
+
# disable returning screenshot if requested
|
|
234
|
+
if self.has_param(item, "no_screenshot"):
|
|
235
|
+
result["no_screenshot"] = True
|
|
236
|
+
|
|
237
|
+
return self.make_response(item, result)
|
|
238
|
+
|
|
117
239
|
def cmd_wait(self, item: dict) -> dict:
|
|
118
240
|
"""
|
|
119
241
|
Wait
|
|
@@ -121,7 +243,10 @@ class Worker(BaseWorker):
|
|
|
121
243
|
:param item: command item
|
|
122
244
|
:return: response item
|
|
123
245
|
"""
|
|
246
|
+
wait_time = 5
|
|
124
247
|
try:
|
|
248
|
+
if self.has_param(item, "seconds"):
|
|
249
|
+
wait_time = int(self.get_param(item, "seconds"))
|
|
125
250
|
self.msg = "Wait"
|
|
126
251
|
self.log(self.msg)
|
|
127
252
|
result = self.get_current(item)
|
|
@@ -133,7 +258,7 @@ class Worker(BaseWorker):
|
|
|
133
258
|
if self.has_param(item, "no_screenshot"):
|
|
134
259
|
result["no_screenshot"] = True
|
|
135
260
|
|
|
136
|
-
time.sleep(
|
|
261
|
+
time.sleep(wait_time)
|
|
137
262
|
return self.make_response(item, result)
|
|
138
263
|
|
|
139
264
|
def cmd_mouse_get_pos(self, item: dict) -> dict:
|
|
@@ -178,20 +303,30 @@ class Worker(BaseWorker):
|
|
|
178
303
|
elif self.has_param(item, "mouse_y"):
|
|
179
304
|
y = self.get_param(item, "mouse_y")
|
|
180
305
|
|
|
306
|
+
# accept normalized 0..999
|
|
307
|
+
try:
|
|
308
|
+
if self._is_normalized_pair(x, y):
|
|
309
|
+
x = self._denorm_x(int(x))
|
|
310
|
+
y = self._denorm_y(int(y))
|
|
311
|
+
except Exception:
|
|
312
|
+
pass
|
|
313
|
+
|
|
181
314
|
if self.has_param(item, "click"):
|
|
182
315
|
click = self.get_param(item, "click")
|
|
183
316
|
if self.has_param(item, "num_clicks"):
|
|
184
317
|
num_clicks = int(self.get_param(item, "num_clicks"))
|
|
185
318
|
try:
|
|
186
319
|
mouse = MouseController()
|
|
187
|
-
mouse.position = (x, y)
|
|
320
|
+
mouse.position = (int(x), int(y))
|
|
188
321
|
if click:
|
|
189
|
-
time.sleep(0.
|
|
322
|
+
time.sleep(0.05)
|
|
190
323
|
self.cmd_mouse_click({
|
|
191
324
|
"cmd": "mouse_click",
|
|
192
325
|
"params": {
|
|
193
326
|
"button": click,
|
|
194
327
|
"num_clicks": num_clicks,
|
|
328
|
+
"x": int(x),
|
|
329
|
+
"y": int(y),
|
|
195
330
|
}
|
|
196
331
|
})
|
|
197
332
|
except Exception as e:
|
|
@@ -220,17 +355,29 @@ class Worker(BaseWorker):
|
|
|
220
355
|
"""
|
|
221
356
|
button = Button.left
|
|
222
357
|
num = 1
|
|
358
|
+
x = None
|
|
359
|
+
y = None
|
|
223
360
|
if self.has_param(item, "button"):
|
|
224
361
|
btn_name = self.get_param(item, "button")
|
|
225
|
-
if btn_name
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
362
|
+
if isinstance(btn_name, str):
|
|
363
|
+
if btn_name.lower() == "middle":
|
|
364
|
+
button = Button.middle
|
|
365
|
+
elif btn_name.lower() == "right":
|
|
366
|
+
button = Button.right
|
|
229
367
|
if self.has_param(item, "num_clicks"):
|
|
230
368
|
num = int(self.get_param(item, "num_clicks"))
|
|
369
|
+
if self.has_param(item, "x") and self.has_param(item, "y"):
|
|
370
|
+
x = int(self.get_param(item, "x"))
|
|
371
|
+
y = int(self.get_param(item, "y"))
|
|
372
|
+
if self._is_normalized_pair(x, y):
|
|
373
|
+
x = self._denorm_x(x)
|
|
374
|
+
y = self._denorm_y(y)
|
|
231
375
|
try:
|
|
232
376
|
mouse = MouseController()
|
|
233
|
-
|
|
377
|
+
if x is not None and y is not None:
|
|
378
|
+
mouse.position = (x, y)
|
|
379
|
+
time.sleep(0.05)
|
|
380
|
+
mouse.click(button, max(1, num))
|
|
234
381
|
result = self.get_current(item)
|
|
235
382
|
self.log("Response: {}".format(result))
|
|
236
383
|
except Exception as e:
|
|
@@ -261,9 +408,14 @@ class Worker(BaseWorker):
|
|
|
261
408
|
y = self.get_param(item, "mouse_y")
|
|
262
409
|
if x is not None and y is not None:
|
|
263
410
|
try:
|
|
411
|
+
# accept normalized 0..999
|
|
412
|
+
xi, yi = int(x), int(y)
|
|
413
|
+
if self._is_normalized_pair(xi, yi):
|
|
414
|
+
xi = self._denorm_x(xi)
|
|
415
|
+
yi = self._denorm_y(yi)
|
|
264
416
|
mouse = MouseController()
|
|
265
|
-
mouse.position = (
|
|
266
|
-
time.sleep(0.
|
|
417
|
+
mouse.position = (xi, yi)
|
|
418
|
+
time.sleep(0.05)
|
|
267
419
|
except Exception as e:
|
|
268
420
|
error = str(e)
|
|
269
421
|
self.log("Error: {}".format(e))
|
|
@@ -273,9 +425,9 @@ class Worker(BaseWorker):
|
|
|
273
425
|
conversion_factor = 30
|
|
274
426
|
delay = 0.01
|
|
275
427
|
if self.has_param(item, "dx"):
|
|
276
|
-
dx = self.get_param(item, "dx")
|
|
428
|
+
dx = int(self.get_param(item, "dx"))
|
|
277
429
|
if self.has_param(item, "dy"):
|
|
278
|
-
dy = self.get_param(item, "dy")
|
|
430
|
+
dy = int(self.get_param(item, "dy"))
|
|
279
431
|
if self.has_param(item, "unit"):
|
|
280
432
|
tmp_unit = self.get_param(item, "unit")
|
|
281
433
|
if tmp_unit in ["step", "px"]:
|
|
@@ -293,14 +445,14 @@ class Worker(BaseWorker):
|
|
|
293
445
|
|
|
294
446
|
# scroll x
|
|
295
447
|
for _ in range(abs(notches_x)):
|
|
296
|
-
|
|
297
|
-
mouse.scroll(
|
|
448
|
+
sdx = 1 if notches_x > 0 else -1
|
|
449
|
+
mouse.scroll(sdx, 0)
|
|
298
450
|
time.sleep(delay)
|
|
299
451
|
|
|
300
452
|
# scroll y
|
|
301
453
|
for _ in range(abs(notches_y)):
|
|
302
|
-
|
|
303
|
-
mouse.scroll(0,
|
|
454
|
+
sdy = 1 if notches_y > 0 else -1
|
|
455
|
+
mouse.scroll(0, sdy)
|
|
304
456
|
time.sleep(delay)
|
|
305
457
|
|
|
306
458
|
print("scrolling: dx={}, dy={}".format(dx, dy))
|
|
@@ -317,7 +469,7 @@ class Worker(BaseWorker):
|
|
|
317
469
|
|
|
318
470
|
def cmd_mouse_drag(self, item: dict) -> dict:
|
|
319
471
|
"""
|
|
320
|
-
Mouse
|
|
472
|
+
Mouse drag
|
|
321
473
|
|
|
322
474
|
:param item: command item
|
|
323
475
|
:return: response item
|
|
@@ -325,18 +477,21 @@ class Worker(BaseWorker):
|
|
|
325
477
|
x = None
|
|
326
478
|
y = None
|
|
327
479
|
if self.has_param(item, "x"):
|
|
328
|
-
x = self.get_param(item, "x")
|
|
480
|
+
x = int(self.get_param(item, "x"))
|
|
329
481
|
elif self.has_param(item, "mouse_x"):
|
|
330
|
-
x = self.get_param(item, "mouse_x")
|
|
482
|
+
x = int(self.get_param(item, "mouse_x"))
|
|
331
483
|
if self.has_param(item, "y"):
|
|
332
|
-
y = self.get_param(item, "y")
|
|
484
|
+
y = int(self.get_param(item, "y"))
|
|
333
485
|
elif self.has_param(item, "mouse_y"):
|
|
334
|
-
y = self.get_param(item, "mouse_y")
|
|
486
|
+
y = int(self.get_param(item, "mouse_y"))
|
|
335
487
|
if x is not None and y is not None:
|
|
336
488
|
try:
|
|
489
|
+
if self._is_normalized_pair(x, y):
|
|
490
|
+
x = self._denorm_x(x)
|
|
491
|
+
y = self._denorm_y(y)
|
|
337
492
|
mouse = MouseController()
|
|
338
493
|
mouse.position = (x, y)
|
|
339
|
-
time.sleep(0.
|
|
494
|
+
time.sleep(0.05)
|
|
340
495
|
except Exception as e:
|
|
341
496
|
error = str(e)
|
|
342
497
|
self.log("Error: {}".format(e))
|
|
@@ -344,14 +499,18 @@ class Worker(BaseWorker):
|
|
|
344
499
|
dy = 0
|
|
345
500
|
delay = 0.02
|
|
346
501
|
if self.has_param(item, "dx"):
|
|
347
|
-
dx = self.get_param(item, "dx")
|
|
502
|
+
dx = int(self.get_param(item, "dx"))
|
|
348
503
|
if self.has_param(item, "dy"):
|
|
349
|
-
dy = self.get_param(item, "dy")
|
|
504
|
+
dy = int(self.get_param(item, "dy"))
|
|
350
505
|
try:
|
|
506
|
+
# If dx,dy are normalized destination, convert
|
|
507
|
+
if self._is_normalized_pair(dx, dy):
|
|
508
|
+
dx = self._denorm_x(dx)
|
|
509
|
+
dy = self._denorm_y(dy)
|
|
351
510
|
mouse = MouseController()
|
|
352
511
|
mouse.press(Button.left)
|
|
353
512
|
time.sleep(delay)
|
|
354
|
-
mouse.position = (dx, dy) #
|
|
513
|
+
mouse.position = (dx, dy) # absolute destination
|
|
355
514
|
time.sleep(delay)
|
|
356
515
|
mouse.release(Button.left)
|
|
357
516
|
print("dragging: dx={}, dy={}".format(dx, dy))
|
|
@@ -368,10 +527,8 @@ class Worker(BaseWorker):
|
|
|
368
527
|
|
|
369
528
|
def cmd_keyboard_keys(self, item: dict) -> dict:
|
|
370
529
|
"""
|
|
371
|
-
Keyboard keys press
|
|
372
|
-
|
|
373
|
-
:param item: command item
|
|
374
|
-
:return: response item
|
|
530
|
+
Keyboard keys press (sequence, single modifier supported)
|
|
531
|
+
For multiple modifiers use key_combination.
|
|
375
532
|
"""
|
|
376
533
|
keyboard = KeyboardController()
|
|
377
534
|
error = None
|
|
@@ -384,7 +541,7 @@ class Worker(BaseWorker):
|
|
|
384
541
|
keys = self.get_param(item, "keys")
|
|
385
542
|
for key in keys:
|
|
386
543
|
if isinstance(key, str):
|
|
387
|
-
if key.lower() in modifiers_list:
|
|
544
|
+
if key.lower() in modifiers_list and modifier is None:
|
|
388
545
|
# check if key is modifier
|
|
389
546
|
if key.lower() == "ctrl" or key.lower() == "control":
|
|
390
547
|
modifier = Key.ctrl
|
|
@@ -463,7 +620,7 @@ class Worker(BaseWorker):
|
|
|
463
620
|
self.set_focus()
|
|
464
621
|
time.sleep(1) # wait for a second
|
|
465
622
|
|
|
466
|
-
if key.lower() == "super" or key.lower() == "start":
|
|
623
|
+
if isinstance(key, str) and (key.lower() == "super" or key.lower() == "start"):
|
|
467
624
|
key = Key.cmd
|
|
468
625
|
|
|
469
626
|
key = self.remap_key(key) # remap key if needed
|
|
@@ -496,7 +653,7 @@ class Worker(BaseWorker):
|
|
|
496
653
|
|
|
497
654
|
def cmd_keyboard_type(self, item: dict) -> dict:
|
|
498
655
|
"""
|
|
499
|
-
Keyboard
|
|
656
|
+
Keyboard type text
|
|
500
657
|
|
|
501
658
|
:param item: command item
|
|
502
659
|
:return: response item
|
|
@@ -607,7 +764,7 @@ class Worker(BaseWorker):
|
|
|
607
764
|
"END": Key.end,
|
|
608
765
|
"HOME": Key.home,
|
|
609
766
|
}
|
|
610
|
-
k = key.upper()
|
|
767
|
+
k = key.upper() if isinstance(key, str) else key
|
|
611
768
|
return mapping.get(k, key)
|
|
612
769
|
|
|
613
770
|
def get_current(self, item: dict = None) -> dict:
|
|
@@ -630,4 +787,224 @@ class Worker(BaseWorker):
|
|
|
630
787
|
'screen_h': screen_y,
|
|
631
788
|
'mouse_x': mouse_pos_x,
|
|
632
789
|
'mouse_y': mouse_pos_y,
|
|
790
|
+
'url': "", # host has no sandbox browser URL
|
|
633
791
|
}
|
|
792
|
+
|
|
793
|
+
# ========================= New Computer Use commands (host-native) ========================= #
|
|
794
|
+
|
|
795
|
+
def cmd_wait_5_seconds(self, item: dict) -> dict:
|
|
796
|
+
return self.cmd_wait({"cmd": "wait", "params": {"seconds": 5}})
|
|
797
|
+
|
|
798
|
+
def cmd_go_back(self, item: dict) -> dict:
|
|
799
|
+
keys = ["cmd", "["] if self._is_mac() else ["alt", "left"]
|
|
800
|
+
return self.cmd_keyboard_keys({"cmd": "keyboard_keys", "params": {"keys": keys}})
|
|
801
|
+
|
|
802
|
+
def cmd_go_forward(self, item: dict) -> dict:
|
|
803
|
+
keys = ["cmd", "]"] if self._is_mac() else ["alt", "right"]
|
|
804
|
+
return self.cmd_keyboard_keys({"cmd": "keyboard_keys", "params": {"keys": keys}})
|
|
805
|
+
|
|
806
|
+
def cmd_search(self, item: dict) -> dict:
|
|
807
|
+
return self.cmd_open_web_browser({"cmd": "open_web_browser", "params": {"url": "https://www.google.com"}})
|
|
808
|
+
|
|
809
|
+
def cmd_navigate(self, item: dict) -> dict:
|
|
810
|
+
url = ""
|
|
811
|
+
if self.has_param(item, "url"):
|
|
812
|
+
url = self.get_param(item, "url") or ""
|
|
813
|
+
return self.cmd_open_web_browser({"cmd": "open_web_browser", "params": {"url": url}})
|
|
814
|
+
|
|
815
|
+
def cmd_click_at(self, item: dict) -> dict:
|
|
816
|
+
x = int(self.get_param(item, "x"))
|
|
817
|
+
y = int(self.get_param(item, "y"))
|
|
818
|
+
px = self._denorm_x(x)
|
|
819
|
+
py = self._denorm_y(y)
|
|
820
|
+
return self.cmd_mouse_move({"cmd": "mouse_move", "params": {"x": px, "y": py, "click": "left", "num_clicks": 1}})
|
|
821
|
+
|
|
822
|
+
def cmd_hover_at(self, item: dict) -> dict:
|
|
823
|
+
x = int(self.get_param(item, "x"))
|
|
824
|
+
y = int(self.get_param(item, "y"))
|
|
825
|
+
px = self._denorm_x(x)
|
|
826
|
+
py = self._denorm_y(y)
|
|
827
|
+
return self.cmd_mouse_move({"cmd": "mouse_move", "params": {"x": px, "y": py}})
|
|
828
|
+
|
|
829
|
+
def cmd_type_text_at(self, item: dict) -> dict:
|
|
830
|
+
x = int(self.get_param(item, "x"))
|
|
831
|
+
y = int(self.get_param(item, "y"))
|
|
832
|
+
px = self._denorm_x(x)
|
|
833
|
+
py = self._denorm_y(y)
|
|
834
|
+
text = self.get_param(item, "text", "") or ""
|
|
835
|
+
press_enter = bool(self.get_param(item, "press_enter", True))
|
|
836
|
+
clear_before = bool(self.get_param(item, "clear_before_typing", True))
|
|
837
|
+
|
|
838
|
+
# focus field
|
|
839
|
+
self.cmd_mouse_move({"cmd": "mouse_move", "params": {"x": px, "y": py, "click": "left", "num_clicks": 1}})
|
|
840
|
+
if clear_before:
|
|
841
|
+
keys = ["cmd", "a"] if self._is_mac() else ["ctrl", "a"]
|
|
842
|
+
self.cmd_keyboard_keys({"cmd": "keyboard_keys", "params": {"keys": keys}})
|
|
843
|
+
self.cmd_keyboard_key({"cmd": "keyboard_key", "params": {"key": "BACKSPACE"}})
|
|
844
|
+
self.cmd_keyboard_type({"cmd": "keyboard_type", "params": {"text": text}})
|
|
845
|
+
if press_enter:
|
|
846
|
+
self.cmd_keyboard_key({"cmd": "keyboard_key", "params": {"key": "ENTER"}})
|
|
847
|
+
return self.make_response(item, self.get_current(item))
|
|
848
|
+
|
|
849
|
+
def cmd_key_combination(self, item: dict) -> dict:
|
|
850
|
+
keyboard = KeyboardController()
|
|
851
|
+
error = None
|
|
852
|
+
|
|
853
|
+
# autofocus on the window
|
|
854
|
+
if self.plugin.get_option_value("auto_focus"):
|
|
855
|
+
self.set_focus()
|
|
856
|
+
time.sleep(1)
|
|
857
|
+
|
|
858
|
+
try:
|
|
859
|
+
raw = self.get_param(item, "keys", [])
|
|
860
|
+
if isinstance(raw, str):
|
|
861
|
+
parts = [p.strip() for p in raw.replace("+", " ").split() if p.strip()]
|
|
862
|
+
else:
|
|
863
|
+
parts = list(raw or [])
|
|
864
|
+
|
|
865
|
+
# Separate modifiers
|
|
866
|
+
mods_map = {
|
|
867
|
+
"ctrl": Key.ctrl, "control": Key.ctrl,
|
|
868
|
+
"alt": Key.alt, "shift": Key.shift,
|
|
869
|
+
"cmd": Key.cmd, "super": Key.cmd, "start": Key.cmd,
|
|
870
|
+
}
|
|
871
|
+
modifiers = []
|
|
872
|
+
keys = []
|
|
873
|
+
for p in parts:
|
|
874
|
+
pl = p.lower()
|
|
875
|
+
if pl in mods_map and mods_map[pl] not in modifiers:
|
|
876
|
+
modifiers.append(mods_map[pl])
|
|
877
|
+
else:
|
|
878
|
+
keys.append(self.remap_key(p))
|
|
879
|
+
|
|
880
|
+
for m in modifiers:
|
|
881
|
+
keyboard.press(m)
|
|
882
|
+
for k in keys:
|
|
883
|
+
keyboard.press(k)
|
|
884
|
+
keyboard.release(k)
|
|
885
|
+
for m in reversed(modifiers):
|
|
886
|
+
keyboard.release(m)
|
|
887
|
+
|
|
888
|
+
result = self.get_current(item)
|
|
889
|
+
except Exception as e:
|
|
890
|
+
result = self.throw_error(e)
|
|
891
|
+
|
|
892
|
+
return self.make_response(item, result)
|
|
893
|
+
|
|
894
|
+
def cmd_scroll_document(self, item: dict) -> dict:
|
|
895
|
+
direction = str(self.get_param(item, "direction", "down")).lower()
|
|
896
|
+
magnitude = int(self.get_param(item, "magnitude", 800))
|
|
897
|
+
dx, dy = 0, 0
|
|
898
|
+
if direction == "down":
|
|
899
|
+
dy = magnitude
|
|
900
|
+
elif direction == "up":
|
|
901
|
+
dy = -magnitude
|
|
902
|
+
elif direction == "left":
|
|
903
|
+
dx = -magnitude
|
|
904
|
+
elif direction == "right":
|
|
905
|
+
dx = magnitude
|
|
906
|
+
return self.cmd_mouse_scroll({"cmd": "mouse_scroll", "params": {"dx": dx, "dy": dy, "unit": "px"}})
|
|
907
|
+
|
|
908
|
+
def cmd_scroll_at(self, item: dict) -> dict:
|
|
909
|
+
direction = str(self.get_param(item, "direction", "down")).lower()
|
|
910
|
+
magnitude = int(self.get_param(item, "magnitude", 800))
|
|
911
|
+
x = self.get_param(item, "x", None)
|
|
912
|
+
y = self.get_param(item, "y", None)
|
|
913
|
+
px = self._denorm_x(int(x)) if x is not None else None
|
|
914
|
+
py = self._denorm_y(int(y)) if y is not None else None
|
|
915
|
+
dx, dy = 0, 0
|
|
916
|
+
if direction == "down":
|
|
917
|
+
dy = magnitude
|
|
918
|
+
elif direction == "up":
|
|
919
|
+
dy = -magnitude
|
|
920
|
+
elif direction == "left":
|
|
921
|
+
dx = -magnitude
|
|
922
|
+
elif direction == "right":
|
|
923
|
+
dx = magnitude
|
|
924
|
+
payload = {"dx": dx, "dy": dy, "unit": "px"}
|
|
925
|
+
if px is not None and py is not None:
|
|
926
|
+
payload["x"] = px
|
|
927
|
+
payload["y"] = py
|
|
928
|
+
return self.cmd_mouse_scroll({"cmd": "mouse_scroll", "params": payload})
|
|
929
|
+
|
|
930
|
+
def cmd_drag_and_drop(self, item: dict) -> dict:
|
|
931
|
+
x = int(self.get_param(item, "x"))
|
|
932
|
+
y = int(self.get_param(item, "y"))
|
|
933
|
+
dx = int(self.get_param(item, "destination_x"))
|
|
934
|
+
dy = int(self.get_param(item, "destination_y"))
|
|
935
|
+
return self.cmd_mouse_drag({
|
|
936
|
+
"cmd": "mouse_drag",
|
|
937
|
+
"params": {
|
|
938
|
+
"x": self._denorm_x(x),
|
|
939
|
+
"y": self._denorm_y(y),
|
|
940
|
+
"dx": self._denorm_x(dx),
|
|
941
|
+
"dy": self._denorm_y(dy),
|
|
942
|
+
}
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
# ========================= Action-style convenience ========================= #
|
|
946
|
+
|
|
947
|
+
def cmd_click(self, item: dict) -> dict:
|
|
948
|
+
p = dict(item.get("params", {}))
|
|
949
|
+
x = p.get("x", None)
|
|
950
|
+
y = p.get("y", None)
|
|
951
|
+
if x is not None and y is not None and self._is_normalized_pair(x, y):
|
|
952
|
+
p["x"] = self._denorm_x(int(x))
|
|
953
|
+
p["y"] = self._denorm_y(int(y))
|
|
954
|
+
p["num_clicks"] = int(p.get("num_clicks", 1))
|
|
955
|
+
return self.cmd_mouse_click({"cmd": "mouse_click", "params": p})
|
|
956
|
+
|
|
957
|
+
def cmd_double_click(self, item: dict) -> dict:
|
|
958
|
+
p = dict(item.get("params", {}))
|
|
959
|
+
p["num_clicks"] = 2
|
|
960
|
+
return self.cmd_click({"cmd": "click", "params": p})
|
|
961
|
+
|
|
962
|
+
def cmd_move(self, item: dict) -> dict:
|
|
963
|
+
p = dict(item.get("params", {}))
|
|
964
|
+
if "x" in p and "y" in p and self._is_normalized_pair(p["x"], p["y"]):
|
|
965
|
+
p["x"] = self._denorm_x(int(p["x"]))
|
|
966
|
+
p["y"] = self._denorm_y(int(p["y"]))
|
|
967
|
+
return self.cmd_mouse_move({"cmd": "mouse_move", "params": p})
|
|
968
|
+
|
|
969
|
+
def cmd_type_text(self, item: dict) -> dict:
|
|
970
|
+
p = dict(item.get("params", {}))
|
|
971
|
+
return self.cmd_keyboard_type({"cmd": "keyboard_type", "params": p})
|
|
972
|
+
|
|
973
|
+
def cmd_keypress(self, item: dict) -> dict:
|
|
974
|
+
p = dict(item.get("params", {}))
|
|
975
|
+
return self.cmd_keyboard_keys({"cmd": "keyboard_keys", "params": p})
|
|
976
|
+
|
|
977
|
+
def cmd_scroll(self, item: dict) -> dict:
|
|
978
|
+
p = dict(item.get("params", {}))
|
|
979
|
+
# accept scroll_x/scroll_y aliases
|
|
980
|
+
if "scroll_x" in p:
|
|
981
|
+
p["dx"] = int(p.get("scroll_x", 0))
|
|
982
|
+
if "scroll_y" in p:
|
|
983
|
+
p["dy"] = int(p.get("scroll_y", 0))
|
|
984
|
+
p["unit"] = "px"
|
|
985
|
+
# normalize optional pointer position
|
|
986
|
+
if "x" in p and "y" in p and self._is_normalized_pair(p["x"], p["y"]):
|
|
987
|
+
p["x"] = self._denorm_x(int(p["x"]))
|
|
988
|
+
p["y"] = self._denorm_y(int(p["y"]))
|
|
989
|
+
return self.cmd_mouse_scroll({"cmd": "mouse_scroll", "params": p})
|
|
990
|
+
|
|
991
|
+
def cmd_drag(self, item: dict) -> dict:
|
|
992
|
+
p = dict(item.get("params", {}))
|
|
993
|
+
path = p.get("path", None)
|
|
994
|
+
if path and isinstance(path, list) and len(path) >= 2:
|
|
995
|
+
x0 = int(path[0]["x"]); y0 = int(path[0]["y"])
|
|
996
|
+
x1 = int(path[1]["x"]); y1 = int(path[1]["y"])
|
|
997
|
+
if self._is_normalized_pair(x0, y0):
|
|
998
|
+
x0 = self._denorm_x(x0); y0 = self._denorm_y(y0)
|
|
999
|
+
if self._is_normalized_pair(x1, y1):
|
|
1000
|
+
x1 = self._denorm_x(x1); y1 = self._denorm_y(y1)
|
|
1001
|
+
return self.cmd_mouse_drag({"cmd": "mouse_drag", "params": {"x": x0, "y": y0, "dx": x1, "dy": y1}})
|
|
1002
|
+
# fallback: explicit x,y and dx,dy
|
|
1003
|
+
if "x" in p and "y" in p and "dx" in p and "dy" in p:
|
|
1004
|
+
x0 = int(p["x"]); y0 = int(p["y"]); x1 = int(p["dx"]); y1 = int(p["dy"])
|
|
1005
|
+
if self._is_normalized_pair(x0, y0):
|
|
1006
|
+
x0 = self._denorm_x(x0); y0 = self._denorm_y(y0)
|
|
1007
|
+
if self._is_normalized_pair(x1, y1):
|
|
1008
|
+
x1 = self._denorm_x(x1); y1 = self._denorm_y(y1)
|
|
1009
|
+
return self.cmd_mouse_drag({"cmd": "mouse_drag", "params": {"x": x0, "y": y0, "dx": x1, "dy": y1}})
|
|
1010
|
+
return self.make_response(item, self.get_current(item))
|