@elizaos/computeruse 0.24.20

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.
@@ -0,0 +1,342 @@
1
+ //! Window manager bindings for Node.js
2
+
3
+ use napi_derive::napi;
4
+ #[cfg(target_os = "windows")]
5
+ use std::sync::Arc;
6
+
7
+ #[cfg(target_os = "windows")]
8
+ use computeruse::WindowManager as RustWindowManager;
9
+
10
+ /// Information about a window
11
+ #[napi(object)]
12
+ pub struct WindowInfo {
13
+ /// Window handle
14
+ pub hwnd: i64,
15
+ /// Process name (e.g., "notepad.exe")
16
+ pub process_name: String,
17
+ /// Process ID
18
+ pub process_id: u32,
19
+ /// Z-order position (0 = topmost)
20
+ pub z_order: u32,
21
+ /// Whether the window is minimized
22
+ pub is_minimized: bool,
23
+ /// Whether the window is maximized
24
+ pub is_maximized: bool,
25
+ /// Whether the window has WS_EX_TOPMOST style
26
+ pub is_always_on_top: bool,
27
+ /// Window title
28
+ pub title: String,
29
+ }
30
+
31
+ #[cfg(target_os = "windows")]
32
+ impl From<computeruse::WindowInfo> for WindowInfo {
33
+ fn from(info: computeruse::WindowInfo) -> Self {
34
+ Self {
35
+ hwnd: info.hwnd as i64,
36
+ process_name: info.process_name,
37
+ process_id: info.process_id,
38
+ z_order: info.z_order,
39
+ is_minimized: info.is_minimized,
40
+ is_maximized: info.is_maximized,
41
+ is_always_on_top: info.is_always_on_top,
42
+ title: info.title,
43
+ }
44
+ }
45
+ }
46
+
47
+ /// Window manager for controlling window states
48
+ ///
49
+ /// Provides functionality for:
50
+ /// - Enumerating windows with Z-order tracking
51
+ /// - Bringing windows to front (bypassing Windows focus-stealing prevention)
52
+ /// - Minimizing/maximizing windows
53
+ /// - Capturing and restoring window states for workflows
54
+ #[napi]
55
+ pub struct WindowManager {
56
+ #[cfg(target_os = "windows")]
57
+ inner: Arc<RustWindowManager>,
58
+ }
59
+
60
+ #[napi]
61
+ impl Default for WindowManager {
62
+ fn default() -> Self {
63
+ Self::new()
64
+ }
65
+ }
66
+
67
+ #[napi]
68
+ impl WindowManager {
69
+ /// Create a new WindowManager instance
70
+ #[napi(constructor)]
71
+ pub fn new() -> Self {
72
+ Self {
73
+ #[cfg(target_os = "windows")]
74
+ inner: Arc::new(RustWindowManager::new()),
75
+ }
76
+ }
77
+
78
+ /// Update window cache with current window information
79
+ #[napi]
80
+ pub async fn update_window_cache(&self) -> napi::Result<()> {
81
+ #[cfg(target_os = "windows")]
82
+ {
83
+ self.inner.update_window_cache().await.map_err(|e| {
84
+ napi::Error::from_reason(format!("Failed to update window cache: {}", e))
85
+ })
86
+ }
87
+ #[cfg(not(target_os = "windows"))]
88
+ {
89
+ Err(napi::Error::from_reason(
90
+ "WindowManager is only supported on Windows",
91
+ ))
92
+ }
93
+ }
94
+
95
+ /// Get topmost window for a process by name
96
+ #[napi]
97
+ pub async fn get_topmost_window_for_process(
98
+ &self,
99
+ #[allow(unused_variables)] process: String,
100
+ ) -> napi::Result<Option<WindowInfo>> {
101
+ #[cfg(target_os = "windows")]
102
+ {
103
+ Ok(self
104
+ .inner
105
+ .get_topmost_window_for_process(&process)
106
+ .await
107
+ .map(WindowInfo::from))
108
+ }
109
+ #[cfg(not(target_os = "windows"))]
110
+ {
111
+ Err(napi::Error::from_reason(
112
+ "WindowManager is only supported on Windows",
113
+ ))
114
+ }
115
+ }
116
+
117
+ /// Get topmost window for a specific PID
118
+ #[napi]
119
+ pub async fn get_topmost_window_for_pid(&self, #[allow(unused_variables)] pid: u32) -> napi::Result<Option<WindowInfo>> {
120
+ #[cfg(target_os = "windows")]
121
+ {
122
+ Ok(self
123
+ .inner
124
+ .get_topmost_window_for_pid(pid)
125
+ .await
126
+ .map(WindowInfo::from))
127
+ }
128
+ #[cfg(not(target_os = "windows"))]
129
+ {
130
+ Err(napi::Error::from_reason(
131
+ "WindowManager is only supported on Windows",
132
+ ))
133
+ }
134
+ }
135
+
136
+ /// Get all visible always-on-top windows
137
+ #[napi]
138
+ pub async fn get_always_on_top_windows(&self) -> napi::Result<Vec<WindowInfo>> {
139
+ #[cfg(target_os = "windows")]
140
+ {
141
+ Ok(self
142
+ .inner
143
+ .get_always_on_top_windows()
144
+ .await
145
+ .into_iter()
146
+ .map(WindowInfo::from)
147
+ .collect())
148
+ }
149
+ #[cfg(not(target_os = "windows"))]
150
+ {
151
+ Err(napi::Error::from_reason(
152
+ "WindowManager is only supported on Windows",
153
+ ))
154
+ }
155
+ }
156
+
157
+ /// Minimize only always-on-top windows (excluding target)
158
+ /// Returns the number of windows minimized
159
+ #[napi]
160
+ pub async fn minimize_always_on_top_windows(&self, #[allow(unused_variables)] target_hwnd: i64) -> napi::Result<u32> {
161
+ #[cfg(target_os = "windows")]
162
+ {
163
+ self.inner
164
+ .minimize_always_on_top_windows(target_hwnd as isize)
165
+ .await
166
+ .map_err(|e| {
167
+ napi::Error::from_reason(format!(
168
+ "Failed to minimize always-on-top windows: {}",
169
+ e
170
+ ))
171
+ })
172
+ }
173
+ #[cfg(not(target_os = "windows"))]
174
+ {
175
+ Err(napi::Error::from_reason(
176
+ "WindowManager is only supported on Windows",
177
+ ))
178
+ }
179
+ }
180
+
181
+ /// Minimize all visible windows except the target
182
+ #[napi]
183
+ pub async fn minimize_all_except(&self, #[allow(unused_variables)] target_hwnd: i64) -> napi::Result<u32> {
184
+ #[cfg(target_os = "windows")]
185
+ {
186
+ self.inner
187
+ .minimize_all_except(target_hwnd as isize)
188
+ .await
189
+ .map_err(|e| napi::Error::from_reason(format!("Failed to minimize windows: {}", e)))
190
+ }
191
+ #[cfg(not(target_os = "windows"))]
192
+ {
193
+ Err(napi::Error::from_reason(
194
+ "WindowManager is only supported on Windows",
195
+ ))
196
+ }
197
+ }
198
+
199
+ /// Maximize window if not already maximized
200
+ /// Returns true if the window was maximized (wasn't already maximized)
201
+ #[napi]
202
+ pub async fn maximize_if_needed(&self, #[allow(unused_variables)] hwnd: i64) -> napi::Result<bool> {
203
+ #[cfg(target_os = "windows")]
204
+ {
205
+ self.inner
206
+ .maximize_if_needed(hwnd as isize)
207
+ .await
208
+ .map_err(|e| napi::Error::from_reason(format!("Failed to maximize window: {}", e)))
209
+ }
210
+ #[cfg(not(target_os = "windows"))]
211
+ {
212
+ Err(napi::Error::from_reason(
213
+ "WindowManager is only supported on Windows",
214
+ ))
215
+ }
216
+ }
217
+
218
+ /// Bring window to front using AttachThreadInput trick
219
+ ///
220
+ /// This uses AttachThreadInput to bypass Windows' focus-stealing prevention.
221
+ /// Returns true if the window is now in the foreground.
222
+ #[napi]
223
+ pub async fn bring_window_to_front(&self, #[allow(unused_variables)] hwnd: i64) -> napi::Result<bool> {
224
+ #[cfg(target_os = "windows")]
225
+ {
226
+ self.inner
227
+ .bring_window_to_front(hwnd as isize)
228
+ .await
229
+ .map_err(|e| {
230
+ napi::Error::from_reason(format!("Failed to bring window to front: {}", e))
231
+ })
232
+ }
233
+ #[cfg(not(target_os = "windows"))]
234
+ {
235
+ Err(napi::Error::from_reason(
236
+ "WindowManager is only supported on Windows",
237
+ ))
238
+ }
239
+ }
240
+
241
+ /// Minimize window if not already minimized
242
+ /// Returns true if the window was minimized (wasn't already minimized)
243
+ #[napi]
244
+ pub async fn minimize_if_needed(&self, #[allow(unused_variables)] hwnd: i64) -> napi::Result<bool> {
245
+ #[cfg(target_os = "windows")]
246
+ {
247
+ self.inner
248
+ .minimize_if_needed(hwnd as isize)
249
+ .await
250
+ .map_err(|e| napi::Error::from_reason(format!("Failed to minimize window: {}", e)))
251
+ }
252
+ #[cfg(not(target_os = "windows"))]
253
+ {
254
+ Err(napi::Error::from_reason(
255
+ "WindowManager is only supported on Windows",
256
+ ))
257
+ }
258
+ }
259
+
260
+ /// Capture current state before workflow
261
+ #[napi]
262
+ pub async fn capture_initial_state(&self) -> napi::Result<()> {
263
+ #[cfg(target_os = "windows")]
264
+ {
265
+ self.inner.capture_initial_state().await.map_err(|e| {
266
+ napi::Error::from_reason(format!("Failed to capture initial state: {}", e))
267
+ })
268
+ }
269
+ #[cfg(not(target_os = "windows"))]
270
+ {
271
+ Err(napi::Error::from_reason(
272
+ "WindowManager is only supported on Windows",
273
+ ))
274
+ }
275
+ }
276
+
277
+ /// Restore windows that were minimized and target window to their original state
278
+ /// Returns the number of windows restored
279
+ #[napi]
280
+ pub async fn restore_all_windows(&self) -> napi::Result<u32> {
281
+ #[cfg(target_os = "windows")]
282
+ {
283
+ self.inner
284
+ .restore_all_windows()
285
+ .await
286
+ .map_err(|e| napi::Error::from_reason(format!("Failed to restore windows: {}", e)))
287
+ }
288
+ #[cfg(not(target_os = "windows"))]
289
+ {
290
+ Err(napi::Error::from_reason(
291
+ "WindowManager is only supported on Windows",
292
+ ))
293
+ }
294
+ }
295
+
296
+ /// Clear captured state
297
+ #[napi]
298
+ pub async fn clear_captured_state(&self) -> napi::Result<()> {
299
+ #[cfg(target_os = "windows")]
300
+ {
301
+ self.inner.clear_captured_state().await;
302
+ Ok(())
303
+ }
304
+ #[cfg(not(target_os = "windows"))]
305
+ {
306
+ Err(napi::Error::from_reason(
307
+ "WindowManager is only supported on Windows",
308
+ ))
309
+ }
310
+ }
311
+
312
+ /// Check if a process is a UWP/Modern app
313
+ #[napi]
314
+ pub async fn is_uwp_app(&self, #[allow(unused_variables)] pid: u32) -> napi::Result<bool> {
315
+ #[cfg(target_os = "windows")]
316
+ {
317
+ Ok(self.inner.is_uwp_app(pid).await)
318
+ }
319
+ #[cfg(not(target_os = "windows"))]
320
+ {
321
+ Err(napi::Error::from_reason(
322
+ "WindowManager is only supported on Windows",
323
+ ))
324
+ }
325
+ }
326
+
327
+ /// Track a window as the target for restoration
328
+ #[napi]
329
+ pub async fn set_target_window(&self, #[allow(unused_variables)] hwnd: i64) -> napi::Result<()> {
330
+ #[cfg(target_os = "windows")]
331
+ {
332
+ self.inner.set_target_window(hwnd as isize).await;
333
+ Ok(())
334
+ }
335
+ #[cfg(not(target_os = "windows"))]
336
+ {
337
+ Err(napi::Error::from_reason(
338
+ "WindowManager is only supported on Windows",
339
+ ))
340
+ }
341
+ }
342
+ }