replace firefox with chrome (#14)

This commit is contained in:
Hari 2026-03-26 15:25:40 -04:00 committed by GitHub
parent 580ea79c27
commit ff26c57035
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 61 additions and 63 deletions

View file

@ -37,9 +37,9 @@ preferred read surface for focused state queries.
## Wait for state transitions ## Wait for state transitions
```sh ```sh
deskctl wait window --selector 'title=Firefox' --timeout 10 deskctl wait window --selector 'title=Chromium' --timeout 10
deskctl wait focus --selector 'id=win3' --timeout 5 deskctl wait focus --selector 'id=win3' --timeout 5
deskctl --json wait window --selector 'class=firefox' --poll-ms 100 deskctl --json wait window --selector 'class=chromium' --poll-ms 100
``` ```
Wait commands return the matched window payload on success. In `--json` mode, Wait commands return the matched window payload on success. In `--json` mode,
@ -48,9 +48,9 @@ timeouts and selector failures expose structured `kind` values.
## Act on windows ## Act on windows
```sh ```sh
deskctl launch firefox deskctl launch chromium
deskctl focus @w1 deskctl focus @w1
deskctl focus 'title=Firefox' deskctl focus 'title=Chromium'
deskctl click @w1 deskctl click @w1
deskctl click 960,540 deskctl click 960,540
deskctl dblclick @w2 deskctl dblclick @w2
@ -86,8 +86,8 @@ more deterministic for automation, and easier to retry safely.
```sh ```sh
ref=w1 ref=w1
id=win1 id=win1
title=Firefox title=Chromium
class=firefox class=chromium
focused focused
``` ```
@ -99,7 +99,7 @@ w1
win1 win1
``` ```
Bare strings like `firefox` are fuzzy matches. They resolve when there is one Bare strings like `chromium` are fuzzy matches. They resolve when there is one
match and fail with candidate windows when there are multiple matches. match and fail with candidate windows when there are multiple matches.
## Global options ## Global options

View file

@ -38,13 +38,13 @@ Prefer explicit selectors when you need deterministic targeting:
```sh ```sh
ref=w1 ref=w1
id=win1 id=win1
title=Firefox title=Chromium
class=firefox class=chromium
focused focused
``` ```
Legacy refs such as `@w1` still work after `snapshot` or `list-windows`. Bare Legacy refs such as `@w1` still work after `snapshot` or `list-windows`. Bare
strings like `firefox` are fuzzy matches and now fail on ambiguity. strings like `chromium` are fuzzy matches and now fail on ambiguity.
## 4. Wait, act, verify ## 4. Wait, act, verify
@ -55,16 +55,16 @@ The core loop is:
deskctl snapshot --annotate deskctl snapshot --annotate
# wait # wait
deskctl wait window --selector 'title=Firefox' --timeout 10 deskctl wait window --selector 'title=Chromium' --timeout 10
# act # act
deskctl focus 'title=Firefox' deskctl focus 'title=Chromium'
deskctl hotkey ctrl l deskctl hotkey ctrl l
deskctl type "https://example.com" deskctl type "https://example.com"
deskctl press enter deskctl press enter
# verify # verify
deskctl wait focus --selector 'title=Firefox' --timeout 5 deskctl wait focus --selector 'title=Chromium' --timeout 5
deskctl snapshot deskctl snapshot
``` ```
@ -84,8 +84,8 @@ Every command supports `--json` and uses the same top-level envelope:
{ {
"ref_id": "w1", "ref_id": "w1",
"window_id": "win1", "window_id": "win1",
"title": "Firefox", "title": "Chromium",
"app_name": "firefox", "app_name": "chromium",
"x": 0, "x": 0,
"y": 0, "y": 0,
"width": 1920, "width": 1920,

View file

@ -30,8 +30,8 @@ Every desktop interaction follows: **observe -> wait -> act -> verify**.
```bash ```bash
deskctl snapshot --annotate # observe deskctl snapshot --annotate # observe
deskctl wait window --selector 'title=Firefox' --timeout 10 # wait deskctl wait window --selector 'title=Chromium' --timeout 10 # wait
deskctl click 'title=Firefox' # act deskctl click 'title=Chromium' # act
deskctl snapshot # verify deskctl snapshot # verify
``` ```
@ -42,12 +42,12 @@ See [workflows/observe-act.sh](workflows/observe-act.sh) for a reusable script.
```bash ```bash
ref=w1 # snapshot ref (short-lived) ref=w1 # snapshot ref (short-lived)
id=win1 # stable window ID (session-scoped) id=win1 # stable window ID (session-scoped)
title=Firefox # match by title title=Chromium # match by title
class=firefox # match by WM class class=chromium # match by WM class
focused # currently focused window focused # currently focused window
``` ```
Bare strings like `firefox` do fuzzy matching but fail on ambiguity. Prefer explicit selectors. Bare strings like `chromium` do fuzzy matching but fail on ambiguity. Prefer explicit selectors.
## References ## References

View file

@ -23,8 +23,8 @@ deskctl get-mouse-position
## Wait ## Wait
```bash ```bash
deskctl wait window --selector 'title=Firefox' --timeout 10 deskctl wait window --selector 'title=Chromium' --timeout 10
deskctl wait focus --selector 'class=firefox' --timeout 5 deskctl wait focus --selector 'class=chromium' --timeout 5
``` ```
Returns the matched window payload on success. Failures include structured Returns the matched window payload on success. Failures include structured
@ -35,8 +35,8 @@ Returns the matched window payload on success. Failures include structured
```bash ```bash
ref=w1 ref=w1
id=win1 id=win1
title=Firefox title=Chromium
class=firefox class=chromium
focused focused
``` ```
@ -46,7 +46,7 @@ on ambiguity.
## Act ## Act
```bash ```bash
deskctl focus 'class=firefox' deskctl focus 'class=chromium'
deskctl click @w1 deskctl click @w1
deskctl dblclick @w2 deskctl dblclick @w2
deskctl type "hello world" deskctl type "hello world"
@ -59,7 +59,7 @@ deskctl mouse drag 100 100 500 500
deskctl move-window @w1 100 120 deskctl move-window @w1 100 120
deskctl resize-window @w1 1280 720 deskctl resize-window @w1 1280 720
deskctl close @w3 deskctl close @w3
deskctl launch firefox deskctl launch chromium
``` ```
The daemon starts automatically on first command. In normal usage you should The daemon starts automatically on first command. In normal usage you should

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# observe-act.sh - main desktop interaction loop # observe-act.sh - main desktop interaction loop
# usage: ./observe-act.sh <selector> [action] [action-args...] # usage: ./observe-act.sh <selector> [action] [action-args...]
# example: ./observe-act.sh 'title=Firefox' click # example: ./observe-act.sh 'title=Chromium' click
# example: ./observe-act.sh 'class=terminal' type "ls -la" # example: ./observe-act.sh 'class=terminal' type "ls -la"
set -euo pipefail set -euo pipefail

View file

@ -48,13 +48,13 @@ pub enum Command {
/// Click a window ref or coordinates /// Click a window ref or coordinates
#[command(after_help = CLICK_EXAMPLES)] #[command(after_help = CLICK_EXAMPLES)]
Click { Click {
/// Selector (ref=w1, id=win1, title=Firefox, class=firefox, focused) or x,y coordinates /// Selector (ref=w1, id=win1, title=Chromium, class=chromium, focused) or x,y coordinates
selector: String, selector: String,
}, },
/// Double-click a window ref or coordinates /// Double-click a window ref or coordinates
#[command(after_help = DBLCLICK_EXAMPLES)] #[command(after_help = DBLCLICK_EXAMPLES)]
Dblclick { Dblclick {
/// Selector (ref=w1, id=win1, title=Firefox, class=firefox, focused) or x,y coordinates /// Selector (ref=w1, id=win1, title=Chromium, class=chromium, focused) or x,y coordinates
selector: String, selector: String,
}, },
/// Type text into the focused window /// Type text into the focused window
@ -81,19 +81,19 @@ pub enum Command {
/// Focus a window by ref or name /// Focus a window by ref or name
#[command(after_help = FOCUS_EXAMPLES)] #[command(after_help = FOCUS_EXAMPLES)]
Focus { Focus {
/// Selector: ref=w1, id=win1, title=Firefox, class=firefox, focused, or a fuzzy substring /// Selector: ref=w1, id=win1, title=Chromium, class=chromium, focused, or a fuzzy substring
selector: String, selector: String,
}, },
/// Close a window by ref or name /// Close a window by ref or name
#[command(after_help = CLOSE_EXAMPLES)] #[command(after_help = CLOSE_EXAMPLES)]
Close { Close {
/// Selector: ref=w1, id=win1, title=Firefox, class=firefox, focused, or a fuzzy substring /// Selector: ref=w1, id=win1, title=Chromium, class=chromium, focused, or a fuzzy substring
selector: String, selector: String,
}, },
/// Move a window /// Move a window
#[command(after_help = MOVE_WINDOW_EXAMPLES)] #[command(after_help = MOVE_WINDOW_EXAMPLES)]
MoveWindow { MoveWindow {
/// Selector: ref=w1, id=win1, title=Firefox, class=firefox, focused, or a fuzzy substring /// Selector: ref=w1, id=win1, title=Chromium, class=chromium, focused, or a fuzzy substring
selector: String, selector: String,
/// X position /// X position
x: i32, x: i32,
@ -103,7 +103,7 @@ pub enum Command {
/// Resize a window /// Resize a window
#[command(after_help = RESIZE_WINDOW_EXAMPLES)] #[command(after_help = RESIZE_WINDOW_EXAMPLES)]
ResizeWindow { ResizeWindow {
/// Selector: ref=w1, id=win1, title=Firefox, class=firefox, focused, or a fuzzy substring /// Selector: ref=w1, id=win1, title=Chromium, class=chromium, focused, or a fuzzy substring
selector: String, selector: String,
/// Width /// Width
w: u32, w: u32,
@ -210,19 +210,19 @@ const SNAPSHOT_EXAMPLES: &str =
const LIST_WINDOWS_EXAMPLES: &str = const LIST_WINDOWS_EXAMPLES: &str =
"Examples:\n deskctl list-windows\n deskctl --json list-windows"; "Examples:\n deskctl list-windows\n deskctl --json list-windows";
const CLICK_EXAMPLES: &str = const CLICK_EXAMPLES: &str =
"Examples:\n deskctl click @w1\n deskctl click 'title=Firefox'\n deskctl click 500,300"; "Examples:\n deskctl click @w1\n deskctl click 'title=Chromium'\n deskctl click 500,300";
const DBLCLICK_EXAMPLES: &str = const DBLCLICK_EXAMPLES: &str =
"Examples:\n deskctl dblclick @w2\n deskctl dblclick 'class=firefox'\n deskctl dblclick 500,300"; "Examples:\n deskctl dblclick @w2\n deskctl dblclick 'class=chromium'\n deskctl dblclick 500,300";
const TYPE_EXAMPLES: &str = const TYPE_EXAMPLES: &str =
"Examples:\n deskctl type \"hello world\"\n deskctl type \"https://example.com\""; "Examples:\n deskctl type \"hello world\"\n deskctl type \"https://example.com\"";
const PRESS_EXAMPLES: &str = "Examples:\n deskctl press enter\n deskctl press escape"; const PRESS_EXAMPLES: &str = "Examples:\n deskctl press enter\n deskctl press escape";
const HOTKEY_EXAMPLES: &str = "Examples:\n deskctl hotkey ctrl l\n deskctl hotkey ctrl shift t"; const HOTKEY_EXAMPLES: &str = "Examples:\n deskctl hotkey ctrl l\n deskctl hotkey ctrl shift t";
const FOCUS_EXAMPLES: &str = const FOCUS_EXAMPLES: &str =
"Examples:\n deskctl focus @w1\n deskctl focus 'title=Firefox'\n deskctl focus focused"; "Examples:\n deskctl focus @w1\n deskctl focus 'title=Chromium'\n deskctl focus focused";
const CLOSE_EXAMPLES: &str = const CLOSE_EXAMPLES: &str =
"Examples:\n deskctl close @w3\n deskctl close 'id=win2'\n deskctl close 'class=firefox'"; "Examples:\n deskctl close @w3\n deskctl close 'id=win2'\n deskctl close 'class=chromium'";
const MOVE_WINDOW_EXAMPLES: &str = const MOVE_WINDOW_EXAMPLES: &str =
"Examples:\n deskctl move-window @w1 100 200\n deskctl move-window 'title=Firefox' 0 0"; "Examples:\n deskctl move-window @w1 100 200\n deskctl move-window 'title=Chromium' 0 0";
const RESIZE_WINDOW_EXAMPLES: &str = const RESIZE_WINDOW_EXAMPLES: &str =
"Examples:\n deskctl resize-window @w1 1280 720\n deskctl resize-window 'id=win2' 800 600"; "Examples:\n deskctl resize-window @w1 1280 720\n deskctl resize-window 'id=win2' 800 600";
const GET_MONITORS_EXAMPLES: &str = const GET_MONITORS_EXAMPLES: &str =
@ -237,12 +237,12 @@ const GET_MOUSE_POSITION_EXAMPLES: &str =
const DOCTOR_EXAMPLES: &str = "Examples:\n deskctl doctor\n deskctl --json doctor"; const DOCTOR_EXAMPLES: &str = "Examples:\n deskctl doctor\n deskctl --json doctor";
const UPGRADE_EXAMPLES: &str = const UPGRADE_EXAMPLES: &str =
"Examples:\n deskctl upgrade\n deskctl upgrade --yes\n deskctl --json upgrade --yes"; "Examples:\n deskctl upgrade\n deskctl upgrade --yes\n deskctl --json upgrade --yes";
const WAIT_WINDOW_EXAMPLES: &str = "Examples:\n deskctl wait window --selector 'title=Firefox' --timeout 10\n deskctl --json wait window --selector 'class=firefox' --poll-ms 100"; const WAIT_WINDOW_EXAMPLES: &str = "Examples:\n deskctl wait window --selector 'title=Chromium' --timeout 10\n deskctl --json wait window --selector 'class=chromium' --poll-ms 100";
const WAIT_FOCUS_EXAMPLES: &str = "Examples:\n deskctl wait focus --selector 'id=win3' --timeout 5\n deskctl wait focus --selector focused --poll-ms 200"; const WAIT_FOCUS_EXAMPLES: &str = "Examples:\n deskctl wait focus --selector 'id=win3' --timeout 5\n deskctl wait focus --selector focused --poll-ms 200";
const SCREENSHOT_EXAMPLES: &str = const SCREENSHOT_EXAMPLES: &str =
"Examples:\n deskctl screenshot\n deskctl screenshot /tmp/screen.png\n deskctl screenshot --annotate"; "Examples:\n deskctl screenshot\n deskctl screenshot /tmp/screen.png\n deskctl screenshot --annotate";
const LAUNCH_EXAMPLES: &str = const LAUNCH_EXAMPLES: &str =
"Examples:\n deskctl launch firefox\n deskctl launch code -- --new-window"; "Examples:\n deskctl launch chromium\n deskctl launch code -- --new-window";
const MOUSE_MOVE_EXAMPLES: &str = const MOUSE_MOVE_EXAMPLES: &str =
"Examples:\n deskctl mouse move 500 300\n deskctl mouse move 0 0"; "Examples:\n deskctl mouse move 500 300\n deskctl mouse move 0 0";
const MOUSE_SCROLL_EXAMPLES: &str = const MOUSE_SCROLL_EXAMPLES: &str =
@ -277,7 +277,7 @@ pub enum WaitCmd {
#[derive(Args)] #[derive(Args)]
pub struct WaitSelectorOpts { pub struct WaitSelectorOpts {
/// Selector: ref=w1, id=win1, title=Firefox, class=firefox, focused, or a fuzzy substring /// Selector: ref=w1, id=win1, title=Chromium, class=chromium, focused, or a fuzzy substring
#[arg(long)] #[arg(long)]
pub selector: String, pub selector: String,
@ -1103,8 +1103,8 @@ mod tests {
"windows": [{ "windows": [{
"ref_id": "w1", "ref_id": "w1",
"window_id": "win1", "window_id": "win1",
"title": "Firefox", "title": "Chromium",
"app_name": "firefox", "app_name": "chromium",
"x": 0, "x": 0,
"y": 0, "y": 0,
"width": 1280, "width": 1280,
@ -1125,37 +1125,37 @@ mod tests {
fn action_text_includes_target_identity() { fn action_text_includes_target_identity() {
let lines = render_success_lines( let lines = render_success_lines(
&Command::Focus { &Command::Focus {
selector: "title=Firefox".to_string(), selector: "title=Chromium".to_string(),
}, },
Some(&json!({ Some(&json!({
"action": "focus", "action": "focus",
"window": "Firefox", "window": "Chromium",
"title": "Firefox", "title": "Chromium",
"ref_id": "w2", "ref_id": "w2",
"window_id": "win7" "window_id": "win7"
})), })),
) )
.unwrap(); .unwrap();
assert_eq!(lines, vec!["Focused @w2 [win7] \"Firefox\""]); assert_eq!(lines, vec!["Focused @w2 [win7] \"Chromium\""]);
} }
#[test] #[test]
fn timeout_errors_render_last_observation() { fn timeout_errors_render_last_observation() {
let lines = render_error_lines(&Response::err_with_data( let lines = render_error_lines(&Response::err_with_data(
"Timed out waiting for focus to match selector: title=Firefox", "Timed out waiting for focus to match selector: title=Chromium",
json!({ json!({
"kind": "timeout", "kind": "timeout",
"wait": "focus", "wait": "focus",
"selector": "title=Firefox", "selector": "title=Chromium",
"timeout_ms": 1000, "timeout_ms": 1000,
"last_observation": { "last_observation": {
"kind": "window_not_focused", "kind": "window_not_focused",
"window": { "window": {
"ref_id": "w1", "ref_id": "w1",
"window_id": "win1", "window_id": "win1",
"title": "Firefox", "title": "Chromium",
"app_name": "firefox", "app_name": "chromium",
"x": 0, "x": 0,
"y": 0, "y": 0,
"width": 1280, "width": 1280,
@ -1167,10 +1167,8 @@ mod tests {
}), }),
)); ));
assert!(lines assert!(lines.iter().any(|line| line
.iter() .contains("Timed out after 1000ms waiting for focus selector title=Chromium")));
.any(|line| line
.contains("Timed out after 1000ms waiting for focus selector title=Firefox")));
assert!(lines assert!(lines
.iter() .iter()
.any(|line| line.contains("matching window exists but is not focused yet"))); .any(|line| line.contains("matching window exists but is not focused yet")));
@ -1190,9 +1188,9 @@ mod tests {
let summary = target_summary(&json!({ let summary = target_summary(&json!({
"ref_id": "w1", "ref_id": "w1",
"window_id": "win1", "window_id": "win1",
"title": "Firefox" "title": "Chromium"
})); }));
assert_eq!(summary.as_deref(), Some("@w1 [win1] \"Firefox\"")); assert_eq!(summary.as_deref(), Some("@w1 [win1] \"Chromium\""));
} }
#[test] #[test]

View file

@ -412,8 +412,8 @@ mod tests {
SelectorQuery::WindowId("win4".to_string()) SelectorQuery::WindowId("win4".to_string())
); );
assert_eq!( assert_eq!(
SelectorQuery::parse("title=Firefox"), SelectorQuery::parse("title=Chromium"),
SelectorQuery::Title("Firefox".to_string()) SelectorQuery::Title("Chromium".to_string())
); );
assert_eq!( assert_eq!(
SelectorQuery::parse("class=Navigator"), SelectorQuery::parse("class=Navigator"),
@ -458,11 +458,11 @@ mod tests {
fn fuzzy_resolution_fails_with_candidates_when_ambiguous() { fn fuzzy_resolution_fails_with_candidates_when_ambiguous() {
let mut refs = RefMap::new(); let mut refs = RefMap::new();
refs.rebuild(&[ refs.rebuild(&[
sample_window(1, "Firefox"), sample_window(1, "Chromium"),
BackendWindow { BackendWindow {
native_id: 2, native_id: 2,
title: "Firefox Settings".to_string(), title: "Chromium Settings".to_string(),
app_name: "Firefox".to_string(), app_name: "Chromium".to_string(),
x: 0, x: 0,
y: 0, y: 0,
width: 10, width: 10,
@ -472,7 +472,7 @@ mod tests {
}, },
]); ]);
match refs.resolve("firefox") { match refs.resolve("chromium") {
ResolveResult::Ambiguous { ResolveResult::Ambiguous {
mode, candidates, .. mode, candidates, ..
} => { } => {