@mediar-ai/terminator 0.20.6
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/Cargo.toml +23 -0
- package/README.md +216 -0
- package/build.rs +4 -0
- package/index.d.ts +738 -0
- package/index.js +321 -0
- package/node_example.js +64 -0
- package/package.json +64 -0
- package/src/desktop.rs +520 -0
- package/src/element.rs +581 -0
- package/src/exceptions.rs +58 -0
- package/src/lib.rs +20 -0
- package/src/locator.rs +172 -0
- package/src/selector.rs +139 -0
- package/src/types.rs +308 -0
- package/sync-version.js +60 -0
- package/test-tree.js +83 -0
- package/tests/comprehensive-ui-elements.test.js +524 -0
- package/tests/element-chaining.test.js +158 -0
- package/tests/element-range.test.js +207 -0
- package/tests/element-scroll-into-view.test.js +256 -0
- package/tests/element-value.test.js +264 -0
- package/tests/locator-validate.test.js +260 -0
- package/tests/locator-waitfor.test.js +286 -0
- package/wrapper.d.ts +42 -0
- package/wrapper.js +170 -0
package/src/desktop.rs
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
use crate::types::{Monitor, MonitorScreenshotPair};
|
|
2
|
+
use crate::Selector;
|
|
3
|
+
use crate::{
|
|
4
|
+
map_error, CommandOutput, Element, Locator, ScreenshotResult, TreeBuildConfig, UINode,
|
|
5
|
+
};
|
|
6
|
+
use napi::bindgen_prelude::Either;
|
|
7
|
+
use napi_derive::napi;
|
|
8
|
+
use std::sync::Once;
|
|
9
|
+
use terminator::Desktop as TerminatorDesktop;
|
|
10
|
+
|
|
11
|
+
/// Main entry point for desktop automation.
|
|
12
|
+
#[napi(js_name = "Desktop")]
|
|
13
|
+
pub struct Desktop {
|
|
14
|
+
inner: TerminatorDesktop,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[allow(clippy::needless_pass_by_value)]
|
|
18
|
+
#[napi]
|
|
19
|
+
impl Desktop {
|
|
20
|
+
/// Create a new Desktop automation instance with configurable options.
|
|
21
|
+
///
|
|
22
|
+
/// @param {boolean} [useBackgroundApps=false] - Enable background apps support.
|
|
23
|
+
/// @param {boolean} [activateApp=false] - Enable app activation support.
|
|
24
|
+
/// @param {string} [logLevel] - Logging level (e.g., 'info', 'debug', 'warn', 'error').
|
|
25
|
+
/// @returns {Desktop} A new Desktop automation instance.
|
|
26
|
+
#[napi(constructor)]
|
|
27
|
+
pub fn new(
|
|
28
|
+
use_background_apps: Option<bool>,
|
|
29
|
+
activate_app: Option<bool>,
|
|
30
|
+
log_level: Option<String>,
|
|
31
|
+
) -> Self {
|
|
32
|
+
let use_background_apps = use_background_apps.unwrap_or(false);
|
|
33
|
+
let activate_app = activate_app.unwrap_or(false);
|
|
34
|
+
let log_level = log_level.unwrap_or_else(|| "warn".to_string());
|
|
35
|
+
static INIT: Once = Once::new();
|
|
36
|
+
INIT.call_once(|| {
|
|
37
|
+
let _ = tracing_subscriber::fmt()
|
|
38
|
+
.with_env_filter(log_level)
|
|
39
|
+
.with_ansi(false) // Disable ANSI color codes for cleaner output
|
|
40
|
+
.try_init();
|
|
41
|
+
});
|
|
42
|
+
let desktop = TerminatorDesktop::new(use_background_apps, activate_app)
|
|
43
|
+
.expect("Failed to create Desktop instance");
|
|
44
|
+
Desktop { inner: desktop }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Get the root UI element of the desktop.
|
|
48
|
+
///
|
|
49
|
+
/// @returns {Element} The root UI element.
|
|
50
|
+
#[napi]
|
|
51
|
+
pub fn root(&self) -> Element {
|
|
52
|
+
let root = self.inner.root();
|
|
53
|
+
Element::from(root)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Get a list of all running applications.
|
|
57
|
+
///
|
|
58
|
+
/// @returns {Array<Element>} List of application UI elements.
|
|
59
|
+
#[napi]
|
|
60
|
+
pub fn applications(&self) -> napi::Result<Vec<Element>> {
|
|
61
|
+
self.inner
|
|
62
|
+
.applications()
|
|
63
|
+
.map(|apps| apps.into_iter().map(Element::from).collect())
|
|
64
|
+
.map_err(map_error)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Get a running application by name.
|
|
68
|
+
///
|
|
69
|
+
/// @param {string} name - The name of the application to find.
|
|
70
|
+
/// @returns {Element} The application UI element.
|
|
71
|
+
#[napi]
|
|
72
|
+
pub fn application(&self, name: String) -> napi::Result<Element> {
|
|
73
|
+
self.inner
|
|
74
|
+
.application(&name)
|
|
75
|
+
.map(Element::from)
|
|
76
|
+
.map_err(map_error)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Open an application by name.
|
|
80
|
+
///
|
|
81
|
+
/// @param {string} name - The name of the application to open.
|
|
82
|
+
#[napi]
|
|
83
|
+
pub fn open_application(&self, name: String) -> napi::Result<Element> {
|
|
84
|
+
self.inner
|
|
85
|
+
.open_application(&name)
|
|
86
|
+
.map(Element::from)
|
|
87
|
+
.map_err(map_error)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Activate an application by name.
|
|
91
|
+
///
|
|
92
|
+
/// @param {string} name - The name of the application to activate.
|
|
93
|
+
#[napi]
|
|
94
|
+
pub fn activate_application(&self, name: String) -> napi::Result<()> {
|
|
95
|
+
self.inner.activate_application(&name).map_err(map_error)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// (async) Run a shell command.
|
|
99
|
+
///
|
|
100
|
+
/// @param {string} [windowsCommand] - Command to run on Windows.
|
|
101
|
+
/// @param {string} [unixCommand] - Command to run on Unix.
|
|
102
|
+
/// @returns {Promise<CommandOutput>} The command output.
|
|
103
|
+
#[napi]
|
|
104
|
+
pub async fn run_command(
|
|
105
|
+
&self,
|
|
106
|
+
windows_command: Option<String>,
|
|
107
|
+
unix_command: Option<String>,
|
|
108
|
+
) -> napi::Result<CommandOutput> {
|
|
109
|
+
self.inner
|
|
110
|
+
.run_command(windows_command.as_deref(), unix_command.as_deref())
|
|
111
|
+
.await
|
|
112
|
+
.map(|r| CommandOutput {
|
|
113
|
+
exit_status: r.exit_status,
|
|
114
|
+
stdout: r.stdout,
|
|
115
|
+
stderr: r.stderr,
|
|
116
|
+
})
|
|
117
|
+
.map_err(map_error)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// (async) Execute a shell command using GitHub Actions-style syntax.
|
|
121
|
+
///
|
|
122
|
+
/// @param {string} command - The command to run (can be single or multi-line).
|
|
123
|
+
/// @param {string} [shell] - Optional shell to use (defaults to PowerShell on Windows, bash on Unix).
|
|
124
|
+
/// @param {string} [workingDirectory] - Optional working directory for the command.
|
|
125
|
+
/// @returns {Promise<CommandOutput>} The command output.
|
|
126
|
+
#[napi]
|
|
127
|
+
pub async fn run(
|
|
128
|
+
&self,
|
|
129
|
+
command: String,
|
|
130
|
+
shell: Option<String>,
|
|
131
|
+
working_directory: Option<String>,
|
|
132
|
+
) -> napi::Result<CommandOutput> {
|
|
133
|
+
self.inner
|
|
134
|
+
.run(
|
|
135
|
+
command.as_str(),
|
|
136
|
+
shell.as_deref(),
|
|
137
|
+
working_directory.as_deref(),
|
|
138
|
+
)
|
|
139
|
+
.await
|
|
140
|
+
.map(|r| CommandOutput {
|
|
141
|
+
exit_status: r.exit_status,
|
|
142
|
+
stdout: r.stdout,
|
|
143
|
+
stderr: r.stderr,
|
|
144
|
+
})
|
|
145
|
+
.map_err(map_error)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// (async) Perform OCR on an image file.
|
|
149
|
+
///
|
|
150
|
+
/// @param {string} imagePath - Path to the image file.
|
|
151
|
+
/// @returns {Promise<string>} The extracted text.
|
|
152
|
+
#[napi]
|
|
153
|
+
pub async fn ocr_image_path(&self, image_path: String) -> napi::Result<String> {
|
|
154
|
+
self.inner
|
|
155
|
+
.ocr_image_path(&image_path)
|
|
156
|
+
.await
|
|
157
|
+
.map_err(map_error)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// (async) Perform OCR on a screenshot.
|
|
161
|
+
///
|
|
162
|
+
/// @param {ScreenshotResult} screenshot - The screenshot to process.
|
|
163
|
+
/// @returns {Promise<string>} The extracted text.
|
|
164
|
+
#[napi]
|
|
165
|
+
pub async fn ocr_screenshot(&self, screenshot: ScreenshotResult) -> napi::Result<String> {
|
|
166
|
+
let rust_screenshot = terminator::ScreenshotResult {
|
|
167
|
+
image_data: screenshot.image_data,
|
|
168
|
+
width: screenshot.width,
|
|
169
|
+
height: screenshot.height,
|
|
170
|
+
monitor: screenshot.monitor.map(|m| terminator::Monitor {
|
|
171
|
+
id: m.id,
|
|
172
|
+
name: m.name,
|
|
173
|
+
is_primary: m.is_primary,
|
|
174
|
+
width: m.width,
|
|
175
|
+
height: m.height,
|
|
176
|
+
x: m.x,
|
|
177
|
+
y: m.y,
|
|
178
|
+
scale_factor: m.scale_factor,
|
|
179
|
+
work_area: None,
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
self.inner
|
|
183
|
+
.ocr_screenshot(&rust_screenshot)
|
|
184
|
+
.await
|
|
185
|
+
.map_err(map_error)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// (async) Get the currently focused browser window.
|
|
189
|
+
///
|
|
190
|
+
/// @returns {Promise<Element>} The current browser window element.
|
|
191
|
+
#[napi]
|
|
192
|
+
pub async fn get_current_browser_window(&self) -> napi::Result<Element> {
|
|
193
|
+
self.inner
|
|
194
|
+
.get_current_browser_window()
|
|
195
|
+
.await
|
|
196
|
+
.map(Element::from)
|
|
197
|
+
.map_err(map_error)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Create a locator for finding UI elements.
|
|
201
|
+
///
|
|
202
|
+
/// @param {string | Selector} selector - The selector.
|
|
203
|
+
/// @returns {Locator} A locator for finding elements.
|
|
204
|
+
#[napi]
|
|
205
|
+
pub fn locator(
|
|
206
|
+
&self,
|
|
207
|
+
#[napi(ts_arg_type = "string | Selector")] selector: Either<String, &Selector>,
|
|
208
|
+
) -> napi::Result<Locator> {
|
|
209
|
+
use napi::bindgen_prelude::Either::*;
|
|
210
|
+
let sel_rust: terminator::selector::Selector = match selector {
|
|
211
|
+
A(sel_str) => sel_str.as_str().into(),
|
|
212
|
+
B(sel_obj) => sel_obj.inner.clone(),
|
|
213
|
+
};
|
|
214
|
+
let loc = self.inner.locator(sel_rust);
|
|
215
|
+
Ok(Locator::from(loc))
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/// (async) Get the currently focused window.
|
|
219
|
+
///
|
|
220
|
+
/// @returns {Promise<Element>} The current window element.
|
|
221
|
+
#[napi]
|
|
222
|
+
pub async fn get_current_window(&self) -> napi::Result<Element> {
|
|
223
|
+
self.inner
|
|
224
|
+
.get_current_window()
|
|
225
|
+
.await
|
|
226
|
+
.map(Element::from)
|
|
227
|
+
.map_err(map_error)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/// (async) Get the currently focused application.
|
|
231
|
+
///
|
|
232
|
+
/// @returns {Promise<Element>} The current application element.
|
|
233
|
+
#[napi]
|
|
234
|
+
pub async fn get_current_application(&self) -> napi::Result<Element> {
|
|
235
|
+
self.inner
|
|
236
|
+
.get_current_application()
|
|
237
|
+
.await
|
|
238
|
+
.map(Element::from)
|
|
239
|
+
.map_err(map_error)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/// Get the currently focused element.
|
|
243
|
+
///
|
|
244
|
+
/// @returns {Element} The focused element.
|
|
245
|
+
#[napi]
|
|
246
|
+
pub fn focused_element(&self) -> napi::Result<Element> {
|
|
247
|
+
self.inner
|
|
248
|
+
.focused_element()
|
|
249
|
+
.map(Element::from)
|
|
250
|
+
.map_err(map_error)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Open a URL in a browser.
|
|
254
|
+
///
|
|
255
|
+
/// @param {string} url - The URL to open.
|
|
256
|
+
/// @param {string} [browser] - The browser to use. Can be "Default", "Chrome", "Firefox", "Edge", "Brave", "Opera", "Vivaldi", or a custom browser path.
|
|
257
|
+
#[napi]
|
|
258
|
+
pub fn open_url(&self, url: String, browser: Option<String>) -> napi::Result<Element> {
|
|
259
|
+
let browser_enum = browser.map(|b| match b.to_lowercase().as_str() {
|
|
260
|
+
"default" => terminator::Browser::Default,
|
|
261
|
+
"chrome" => terminator::Browser::Chrome,
|
|
262
|
+
"firefox" => terminator::Browser::Firefox,
|
|
263
|
+
"edge" => terminator::Browser::Edge,
|
|
264
|
+
"brave" => terminator::Browser::Brave,
|
|
265
|
+
"opera" => terminator::Browser::Opera,
|
|
266
|
+
"vivaldi" => terminator::Browser::Vivaldi,
|
|
267
|
+
custom => terminator::Browser::Custom(custom.to_string()),
|
|
268
|
+
});
|
|
269
|
+
self.inner
|
|
270
|
+
.open_url(&url, browser_enum)
|
|
271
|
+
.map(Element::from)
|
|
272
|
+
.map_err(map_error)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Open a file with its default application.
|
|
276
|
+
///
|
|
277
|
+
/// @param {string} filePath - Path to the file to open.
|
|
278
|
+
#[napi]
|
|
279
|
+
pub fn open_file(&self, file_path: String) -> napi::Result<()> {
|
|
280
|
+
self.inner.open_file(&file_path).map_err(map_error)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/// Activate a browser window by title.
|
|
284
|
+
///
|
|
285
|
+
/// @param {string} title - The window title to match.
|
|
286
|
+
#[napi]
|
|
287
|
+
pub fn activate_browser_window_by_title(&self, title: String) -> napi::Result<()> {
|
|
288
|
+
self.inner
|
|
289
|
+
.activate_browser_window_by_title(&title)
|
|
290
|
+
.map_err(map_error)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/// Get the UI tree for a window identified by process ID and optional title.
|
|
294
|
+
///
|
|
295
|
+
/// @param {number} pid - Process ID of the target application.
|
|
296
|
+
/// @param {string} [title] - Optional window title filter.
|
|
297
|
+
/// @param {TreeBuildConfig} [config] - Optional configuration for tree building.
|
|
298
|
+
/// @returns {UINode} Complete UI tree starting from the identified window.
|
|
299
|
+
#[napi]
|
|
300
|
+
pub fn get_window_tree(
|
|
301
|
+
&self,
|
|
302
|
+
pid: u32,
|
|
303
|
+
title: Option<String>,
|
|
304
|
+
config: Option<TreeBuildConfig>,
|
|
305
|
+
) -> napi::Result<UINode> {
|
|
306
|
+
let rust_config = config.map(|c| c.into());
|
|
307
|
+
self.inner
|
|
308
|
+
.get_window_tree(pid, title.as_deref(), rust_config)
|
|
309
|
+
.map(UINode::from)
|
|
310
|
+
.map_err(map_error)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ============== NEW MONITOR METHODS ==============
|
|
314
|
+
|
|
315
|
+
/// (async) List all available monitors/displays.
|
|
316
|
+
///
|
|
317
|
+
/// @returns {Promise<Array<Monitor>>} List of monitor information.
|
|
318
|
+
#[napi]
|
|
319
|
+
pub async fn list_monitors(&self) -> napi::Result<Vec<Monitor>> {
|
|
320
|
+
self.inner
|
|
321
|
+
.list_monitors()
|
|
322
|
+
.await
|
|
323
|
+
.map(|monitors| monitors.into_iter().map(Monitor::from).collect())
|
|
324
|
+
.map_err(map_error)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// (async) Get the primary monitor.
|
|
328
|
+
///
|
|
329
|
+
/// @returns {Promise<Monitor>} Primary monitor information.
|
|
330
|
+
#[napi]
|
|
331
|
+
pub async fn get_primary_monitor(&self) -> napi::Result<Monitor> {
|
|
332
|
+
self.inner
|
|
333
|
+
.get_primary_monitor()
|
|
334
|
+
.await
|
|
335
|
+
.map(Monitor::from)
|
|
336
|
+
.map_err(map_error)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/// (async) Get the monitor containing the currently focused window.
|
|
340
|
+
///
|
|
341
|
+
/// @returns {Promise<Monitor>} Active monitor information.
|
|
342
|
+
#[napi]
|
|
343
|
+
pub async fn get_active_monitor(&self) -> napi::Result<Monitor> {
|
|
344
|
+
self.inner
|
|
345
|
+
.get_active_monitor()
|
|
346
|
+
.await
|
|
347
|
+
.map(Monitor::from)
|
|
348
|
+
.map_err(map_error)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/// (async) Get a monitor by its ID.
|
|
352
|
+
///
|
|
353
|
+
/// @param {string} id - The monitor ID to find.
|
|
354
|
+
/// @returns {Promise<Monitor>} Monitor information.
|
|
355
|
+
#[napi]
|
|
356
|
+
pub async fn get_monitor_by_id(&self, id: String) -> napi::Result<Monitor> {
|
|
357
|
+
self.inner
|
|
358
|
+
.get_monitor_by_id(&id)
|
|
359
|
+
.await
|
|
360
|
+
.map(Monitor::from)
|
|
361
|
+
.map_err(map_error)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/// (async) Get a monitor by its name.
|
|
365
|
+
///
|
|
366
|
+
/// @param {string} name - The monitor name to find.
|
|
367
|
+
/// @returns {Promise<Monitor>} Monitor information.
|
|
368
|
+
#[napi]
|
|
369
|
+
pub async fn get_monitor_by_name(&self, name: String) -> napi::Result<Monitor> {
|
|
370
|
+
self.inner
|
|
371
|
+
.get_monitor_by_name(&name)
|
|
372
|
+
.await
|
|
373
|
+
.map(Monitor::from)
|
|
374
|
+
.map_err(map_error)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/// (async) Capture a screenshot of a specific monitor.
|
|
378
|
+
///
|
|
379
|
+
/// @param {Monitor} monitor - The monitor to capture.
|
|
380
|
+
/// @returns {Promise<ScreenshotResult>} The screenshot data.
|
|
381
|
+
#[napi]
|
|
382
|
+
pub async fn capture_monitor(&self, monitor: Monitor) -> napi::Result<ScreenshotResult> {
|
|
383
|
+
let rust_monitor = terminator::Monitor {
|
|
384
|
+
id: monitor.id,
|
|
385
|
+
name: monitor.name,
|
|
386
|
+
is_primary: monitor.is_primary,
|
|
387
|
+
width: monitor.width,
|
|
388
|
+
height: monitor.height,
|
|
389
|
+
x: monitor.x,
|
|
390
|
+
y: monitor.y,
|
|
391
|
+
scale_factor: monitor.scale_factor,
|
|
392
|
+
work_area: None,
|
|
393
|
+
};
|
|
394
|
+
self.inner
|
|
395
|
+
.capture_monitor(&rust_monitor)
|
|
396
|
+
.await
|
|
397
|
+
.map(|r| ScreenshotResult {
|
|
398
|
+
width: r.width,
|
|
399
|
+
height: r.height,
|
|
400
|
+
image_data: r.image_data,
|
|
401
|
+
monitor: r.monitor.map(Monitor::from),
|
|
402
|
+
})
|
|
403
|
+
.map_err(map_error)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/// (async) Capture screenshots of all monitors.
|
|
407
|
+
///
|
|
408
|
+
/// @returns {Promise<Array<{monitor: Monitor, screenshot: ScreenshotResult}>>} Array of monitor and screenshot pairs.
|
|
409
|
+
#[napi]
|
|
410
|
+
pub async fn capture_all_monitors(&self) -> napi::Result<Vec<MonitorScreenshotPair>> {
|
|
411
|
+
self.inner
|
|
412
|
+
.capture_all_monitors()
|
|
413
|
+
.await
|
|
414
|
+
.map(|results| {
|
|
415
|
+
results
|
|
416
|
+
.into_iter()
|
|
417
|
+
.map(|(monitor, screenshot)| MonitorScreenshotPair {
|
|
418
|
+
monitor: Monitor::from(monitor),
|
|
419
|
+
screenshot: ScreenshotResult {
|
|
420
|
+
width: screenshot.width,
|
|
421
|
+
height: screenshot.height,
|
|
422
|
+
image_data: screenshot.image_data,
|
|
423
|
+
monitor: screenshot.monitor.map(Monitor::from),
|
|
424
|
+
},
|
|
425
|
+
})
|
|
426
|
+
.collect()
|
|
427
|
+
})
|
|
428
|
+
.map_err(map_error)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// (async) Get all window elements for a given application name.
|
|
432
|
+
///
|
|
433
|
+
/// @param {string} name - The name of the application whose windows will be retrieved.
|
|
434
|
+
/// @returns {Promise<Array<Element>>} A list of window elements belonging to the application.
|
|
435
|
+
#[napi]
|
|
436
|
+
pub async fn windows_for_application(&self, name: String) -> napi::Result<Vec<Element>> {
|
|
437
|
+
self.inner
|
|
438
|
+
.windows_for_application(&name)
|
|
439
|
+
.await
|
|
440
|
+
.map(|windows| windows.into_iter().map(Element::from).collect())
|
|
441
|
+
.map_err(map_error)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ============== ADDITIONAL MISSING METHODS ==============
|
|
445
|
+
|
|
446
|
+
/// (async) Get the UI tree for all open applications in parallel.
|
|
447
|
+
///
|
|
448
|
+
/// @returns {Promise<Array<UINode>>} List of UI trees for all applications.
|
|
449
|
+
#[napi]
|
|
450
|
+
pub async fn get_all_applications_tree(&self) -> napi::Result<Vec<UINode>> {
|
|
451
|
+
self.inner
|
|
452
|
+
.get_all_applications_tree()
|
|
453
|
+
.await
|
|
454
|
+
.map(|trees| trees.into_iter().map(UINode::from).collect())
|
|
455
|
+
.map_err(map_error)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/// (async) Press a key globally.
|
|
459
|
+
///
|
|
460
|
+
/// @param {string} key - The key to press (e.g., "Enter", "Ctrl+C", "F1").
|
|
461
|
+
#[napi]
|
|
462
|
+
pub async fn press_key(&self, key: String) -> napi::Result<()> {
|
|
463
|
+
self.inner.press_key(&key).await.map_err(map_error)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/// (async) Execute JavaScript in the currently focused browser tab.
|
|
467
|
+
/// Automatically finds the active browser window and executes the script.
|
|
468
|
+
///
|
|
469
|
+
/// @param {string} script - The JavaScript code to execute in browser context.
|
|
470
|
+
/// @returns {Promise<string>} The result of script execution.
|
|
471
|
+
#[napi]
|
|
472
|
+
pub async fn execute_browser_script(&self, script: String) -> napi::Result<String> {
|
|
473
|
+
self.inner
|
|
474
|
+
.execute_browser_script(&script)
|
|
475
|
+
.await
|
|
476
|
+
.map_err(map_error)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/// (async) Delay execution for a specified number of milliseconds.
|
|
480
|
+
/// Useful for waiting between actions to ensure UI stability.
|
|
481
|
+
///
|
|
482
|
+
/// @param {number} delayMs - Delay in milliseconds.
|
|
483
|
+
/// @returns {Promise<void>}
|
|
484
|
+
#[napi]
|
|
485
|
+
pub async fn delay(&self, delay_ms: u32) -> napi::Result<()> {
|
|
486
|
+
self.inner.delay(delay_ms as u64).await.map_err(map_error)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/// Navigate to a URL in a browser.
|
|
490
|
+
/// This is the recommended method for browser navigation - more reliable than
|
|
491
|
+
/// manually manipulating the address bar with keyboard/mouse actions.
|
|
492
|
+
///
|
|
493
|
+
/// @param {string} url - URL to navigate to
|
|
494
|
+
/// @param {string | null} browser - Optional browser name ('Chrome', 'Firefox', 'Edge', 'Brave', 'Opera', 'Vivaldi', or 'Default')
|
|
495
|
+
/// @returns {Promise<Element>} The browser window element
|
|
496
|
+
#[napi]
|
|
497
|
+
pub fn navigate_browser(&self, url: String, browser: Option<String>) -> napi::Result<Element> {
|
|
498
|
+
let browser_enum = browser.map(|b| match b.as_str() {
|
|
499
|
+
"Chrome" => terminator::Browser::Chrome,
|
|
500
|
+
"Firefox" => terminator::Browser::Firefox,
|
|
501
|
+
"Edge" => terminator::Browser::Edge,
|
|
502
|
+
"Brave" => terminator::Browser::Brave,
|
|
503
|
+
"Opera" => terminator::Browser::Opera,
|
|
504
|
+
"Vivaldi" => terminator::Browser::Vivaldi,
|
|
505
|
+
"Default" => terminator::Browser::Default,
|
|
506
|
+
custom => terminator::Browser::Custom(custom.to_string()),
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
let element = self.inner.open_url(&url, browser_enum).map_err(map_error)?;
|
|
510
|
+
Ok(Element { inner: element })
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/// (async) Set the zoom level to a specific percentage.
|
|
514
|
+
///
|
|
515
|
+
/// @param {number} percentage - The zoom percentage (e.g., 100 for 100%, 150 for 150%, 50 for 50%).
|
|
516
|
+
#[napi]
|
|
517
|
+
pub async fn set_zoom(&self, percentage: u32) -> napi::Result<()> {
|
|
518
|
+
self.inner.set_zoom(percentage).await.map_err(map_error)
|
|
519
|
+
}
|
|
520
|
+
}
|