Skip to main content

mermaid_cli/providers/tool/computer_use/
list_windows.rs

1//! `list_windows` — enumerate visible window titles. X11 only;
2//! Wayland's wlr-protocols ecosystem has no portable window
3//! enumeration primitive.
4
5use std::sync::Arc;
6use std::time::Instant;
7
8use async_trait::async_trait;
9use serde_json::Value;
10
11use crate::domain::{ToolDefinition, ToolOutcome};
12use crate::providers::ctx::ExecContext;
13
14use super::super::ToolExecutor;
15use super::computer_use_success;
16use super::driver::ComputerUseDriver;
17
18pub struct ListWindowsTool {
19    driver: Arc<ComputerUseDriver>,
20}
21
22impl ListWindowsTool {
23    pub fn new(driver: Arc<ComputerUseDriver>) -> Self {
24        Self { driver }
25    }
26}
27
28#[async_trait]
29impl ToolExecutor for ListWindowsTool {
30    fn name(&self) -> &'static str {
31        "list_windows"
32    }
33
34    fn schema(&self) -> ToolDefinition {
35        ToolDefinition {
36            name: "list_windows".to_string(),
37            description: "List visible window titles. X11 only. On Wayland, use screenshot mode \
38                 'fullscreen' or 'monitor' instead — window enumeration is not portable \
39                 across compositors."
40                .to_string(),
41            input_schema: serde_json::json!({ "type": "object", "properties": {} }),
42        }
43    }
44
45    async fn execute(&self, args: Value, ctx: ExecContext) -> ToolOutcome {
46        let started = Instant::now();
47        if let Err(error) = self.driver.ensure_alive() {
48            return ToolOutcome::error(error, started.elapsed().as_secs_f64());
49        }
50
51        let windows = tokio::select! {
52            biased;
53            _ = ctx.token.cancelled() => return ToolOutcome::cancelled(),
54            r = self.driver.list_windows(&ctx.token) => match r {
55                Ok(w) => w,
56                Err(e) => return ToolOutcome::error(
57                    format!("list_windows failed: {}", e),
58                    started.elapsed().as_secs_f64(),
59                ),
60            },
61        };
62
63        let output = if windows.is_empty() {
64            "No visible windows found.".to_string()
65        } else {
66            let list = windows
67                .iter()
68                .map(|w| format!("  - {}", w))
69                .collect::<Vec<_>>()
70                .join("\n");
71            format!("Visible windows ({}):\n{}", windows.len(), list)
72        };
73
74        computer_use_success(
75            "list_windows",
76            args,
77            output,
78            started.elapsed().as_secs_f64(),
79        )
80    }
81}