Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ It is a native binary that uses the Scripting Bridge to communicate with Chrome.
chrome-cli has been tested with the following browsers:

- Chrome
- Chrome Beta
- Chrome Canary
- Chromium
- Brave
Expand Down Expand Up @@ -44,6 +45,7 @@ brew install chrome-cli
This will install:

- chrome-cli
- chrome-beta-cli
- chrome-canary-cli
- chromium-cli
- brave-cli
Expand All @@ -63,20 +65,20 @@ More details [here](https://www.chromium.org/developers/applescript). Thanks to
chrome-cli help (Print help)
chrome-cli list windows (List all windows)
chrome-cli list tabs (List all tabs)
chrome-cli list tabs -w <id> (List tabs in specific window)
chrome-cli list tabs -w <id|name> (List tabs in specific window)
chrome-cli list links (List all tabs' link)
chrome-cli list links -w <id> (List tabs' link in specific window)
chrome-cli list links -w <id|name> (List tabs' link in specific window)
chrome-cli info (Print info for active tab)
chrome-cli info -t <id> (Print info for specific tab)
chrome-cli open <url> (Open url in new tab)
chrome-cli open <url> -n (Open url in new window)
chrome-cli open <url> -i (Open url in new incognito window)
chrome-cli open <url> -t <id> (Open url in specific tab)
chrome-cli open <url> -w <id> (Open url in new tab in specific window)
chrome-cli open <url> -w <id|name> (Open url in new tab in specific window)
chrome-cli close (Close active tab)
chrome-cli close -w (Close active window)
chrome-cli close -t <id> (Close specific tab)
chrome-cli close -w <id> (Close specific window)
chrome-cli close -w <id|name> (Close specific window)
chrome-cli reload (Reload active tab)
chrome-cli reload -t <id> (Reload specific tab)
chrome-cli back (Navigate back in active tab)
Expand All @@ -91,13 +93,13 @@ More details [here](https://www.chromium.org/developers/applescript). Thanks to
chrome-cli presentation -t <id> (Enter presentation mode with a specific tab)
chrome-cli presentation exit (Exit presentation mode)
chrome-cli size (Print size of active window)
chrome-cli size -w <id> (Print size of specific window)
chrome-cli size -w <id|name> (Print size of specific window)
chrome-cli size <width> <height> (Set size of active window)
chrome-cli size <width> <height> -w <id> (Set size of specific window)
chrome-cli size <width> <height> -w <id|name> (Set size of specific window)
chrome-cli position (Print position of active window)
chrome-cli position -w <id> (Print position of specific window)
chrome-cli position -w <id|name> (Print position of specific window)
chrome-cli position <x> <y> (Set position of active window)
chrome-cli position <x> <y> -w <id> (Set position of specific window)
chrome-cli position <x> <y> -w <id|name> (Set position of specific window)
chrome-cli source (Print source from active tab)
chrome-cli source -t <id> (Print source from specific tab)
chrome-cli execute <javascript> (Execute javascript in active tab)
Expand Down Expand Up @@ -125,6 +127,20 @@ $ OUTPUT_FORMAT=json chrome-cli list tabs
}
```

#### Window selection by name

The `-w` flag accepts either a window ID or a window name. When a non-numeric value is provided, it matches windows whose title starts with the given string (case-insensitive). If multiple windows match, the one with the lowest window ID is selected.

```bash
# By window ID
chrome-cli list tabs -w 1869578514

# By window name (case-insensitive prefix match)
chrome-cli list tabs -w Gmail
chrome-cli list tabs -w "Hacker News"
chrome-cli close -w GitHub
```

## Examples

###### List tabs
Expand Down
74 changes: 58 additions & 16 deletions chrome-cli/App.m
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ - (void)listTabsWithLink:(Arguments *)args {
}

- (void)listTabsInWindow:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
chromeWindow *window = [self findWindow:windowId];
NSString *windowArg = [args asString:@"id|name"];
chromeWindow *window = [self findWindowByIdOrName:windowArg];

if (!window) {
return;
Expand Down Expand Up @@ -219,8 +219,8 @@ - (void)listTabsInWindow:(Arguments *)args {
}

- (void)listTabsLinksInWindow:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
chromeWindow *window = [self findWindow:windowId];
NSString *windowArg = [args asString:@"id|name"];
chromeWindow *window = [self findWindowByIdOrName:windowArg];

if (!window) {
return;
Expand Down Expand Up @@ -309,11 +309,11 @@ - (void)openUrlInTab:(Arguments *)args {
}

- (void)openUrlInWindow:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
NSString *windowArg = [args asString:@"id|name"];
NSString *url = [args asString:@"url"];

chromeTab *tab = [[[self.chrome classForScriptingClass:@"tab"] alloc] init];
chromeWindow *window = [self findWindow:windowId];
chromeWindow *window = [self findWindowByIdOrName:windowArg];

if (!window) {
return;
Expand Down Expand Up @@ -368,8 +368,8 @@ - (void)closeActiveWindow:(Arguments *)args {
}

- (void)closeWindow:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
chromeWindow *window = [self findWindow:windowId];
NSString *windowArg = [args asString:@"id|name"];
chromeWindow *window = [self findWindowByIdOrName:windowArg];

if (window) {
[window close];
Expand Down Expand Up @@ -498,8 +498,8 @@ - (void)printActiveWindowSize:(Arguments *)args {
}

- (void)printWindowSize:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
chromeWindow *window = [self findWindow:windowId];
NSString *windowArg = [args asString:@"id|name"];
chromeWindow *window = [self findWindowByIdOrName:windowArg];
CGSize size = window.bounds.size;
if (self->outputFormat == kOutputFormatJSON) {
NSDictionary *output = @{
Expand All @@ -522,11 +522,11 @@ - (void)setActiveWindowSize:(Arguments *)args {
}

- (void)setWindowSize:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
NSString *windowArg = [args asString:@"id|name"];
float width = [args asFloat:@"width"];
float height = [args asFloat:@"height"];

chromeWindow *window = [self findWindow:windowId];
chromeWindow *window = [self findWindowByIdOrName:windowArg];
CGPoint origin = window.bounds.origin;
window.bounds = NSMakeRect(origin.x, origin.y, width, height);
}
Expand All @@ -547,8 +547,8 @@ - (void)printActiveWindowPosition:(Arguments *)args {
}

- (void)printWindowPosition:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
chromeWindow *window = [self findWindow:windowId];
NSString *windowArg = [args asString:@"id|name"];
chromeWindow *window = [self findWindowByIdOrName:windowArg];
CGPoint origin = window.bounds.origin;

if (self->outputFormat == kOutputFormatJSON) {
Expand All @@ -572,11 +572,11 @@ - (void)setActiveWindowPosition:(Arguments *)args {
}

- (void)setWindowPosition:(Arguments *)args {
NSInteger windowId = [args asInteger:@"id"];
NSString *windowArg = [args asString:@"id|name"];
float x = [args asFloat:@"x"];
float y = [args asFloat:@"y"];

chromeWindow *window = [self findWindow:windowId];
chromeWindow *window = [self findWindowByIdOrName:windowArg];
CGSize size = window.bounds.size;
window.bounds = NSMakeRect(x, y, size.width, size.height);
}
Expand Down Expand Up @@ -806,6 +806,48 @@ - (chromeWindow *)findWindow:(NSInteger)windowId {
return nil;
}

- (chromeWindow *)findWindowByName:(NSString *)name {
chromeWindow *bestMatch = nil;
NSInteger bestMatchId = NSIntegerMax;
NSString *lowerName = [name lowercaseString];

for (chromeWindow *window in self.chrome.windows) {
NSString *windowName = [window.name lowercaseString];

// Check if window name starts with search string
if ([windowName hasPrefix:lowerName]) {
NSInteger windowId = [window.id integerValue];

// Prefer lower window ID for ties (all matches have same prefix length)
if (bestMatch == nil || windowId < bestMatchId) {
bestMatch = window;
bestMatchId = windowId;
}
}
}

return bestMatch;
}

- (chromeWindow *)findWindowByIdOrName:(NSString *)identifier {
// Check if identifier is purely numeric
NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
BOOL isNumeric = ([identifier rangeOfCharacterFromSet:nonDigits].location == NSNotFound
&& identifier.length > 0);

if (isNumeric) {
// Try by ID first
chromeWindow *window = [self findWindow:[identifier integerValue]];
if (window) {
return window;
}
// Fall back to name search (for windows with all-numeric titles like "2024")
return [self findWindowByName:identifier];
} else {
return [self findWindowByName:identifier];
}
}

- (chromeTab *)activeTab {
return [self activeWindow].activeTab;
}
Expand Down
16 changes: 8 additions & 8 deletions chrome-cli/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ int main(int argc, const char * argv[])

[argonaut add:@"list windows" target:app action:@selector(listWindows:) description:@"List all windows"];
[argonaut add:@"list tabs" target:app action:@selector(listTabs:) description:@"List all tabs"];
[argonaut add:@"list tabs -w <id>" target:app action:@selector(listTabsInWindow:) description:@"List tabs in specific window"];
[argonaut add:@"list tabs -w <id|name>" target:app action:@selector(listTabsInWindow:) description:@"List tabs in specific window"];
[argonaut add:@"list links" target:app action:@selector(listTabsLinks:) description:@"List all tabs' link"];
[argonaut add:@"list links -w <id>" target:app action:@selector(listTabsLinksInWindow:) description:@"List tabs' link in specific window"];
[argonaut add:@"list links -w <id|name>" target:app action:@selector(listTabsLinksInWindow:) description:@"List tabs' link in specific window"];
[argonaut add:@"list tablinks" target:app action:@selector(listTabsWithLink:) description:@"List tabs' with the link"];


Expand All @@ -51,12 +51,12 @@ int main(int argc, const char * argv[])
[argonaut add:@"open <url> -n" target:app action:@selector(openUrlInNewWindow:) description:@"Open url in new window"];
[argonaut add:@"open <url> -i" target:app action:@selector(openUrlInNewIncognitoWindow:) description:@"Open url in new incognito window"];
[argonaut add:@"open <url> -t <id>" target:app action:@selector(openUrlInTab:) description:@"Open url in specific tab"];
[argonaut add:@"open <url> -w <id>" target:app action:@selector(openUrlInWindow:) description:@"Open url in new tab in specific window"];
[argonaut add:@"open <url> -w <id|name>" target:app action:@selector(openUrlInWindow:) description:@"Open url in new tab in specific window"];

[argonaut add:@"close" target:app action:@selector(closeActiveTab:) description:@"Close active tab"];
[argonaut add:@"close -w" target:app action:@selector(closeActiveWindow:) description:@"Close active window"];
[argonaut add:@"close -t <id>" target:app action:@selector(closeTab:) description:@"Close specific tab"];
[argonaut add:@"close -w <id>" target:app action:@selector(closeWindow:) description:@"Close specific window"];
[argonaut add:@"close -w <id|name>" target:app action:@selector(closeWindow:) description:@"Close specific window"];

[argonaut add:@"reload" target:app action:@selector(reloadActiveTab:) description:@"Reload active tab"];
[argonaut add:@"reload -t <id>" target:app action:@selector(reloadTab:) description:@"Reload specific tab"];
Expand All @@ -71,14 +71,14 @@ int main(int argc, const char * argv[])
[argonaut add:@"activate -t <id> --focus" target:app action:@selector(activateTabAndFocus:) description:@"Activate tab and bring its window to front"];

[argonaut add:@"size" target:app action:@selector(printActiveWindowSize:) description:@"Print size of active window"];
[argonaut add:@"size -w <id>" target:app action:@selector(printWindowSize:) description:@"Print size of specific window"];
[argonaut add:@"size -w <id|name>" target:app action:@selector(printWindowSize:) description:@"Print size of specific window"];
[argonaut add:@"size <width> <height>" target:app action:@selector(setActiveWindowSize:) description:@"Set size of active window"];
[argonaut add:@"size <width> <height> -w <id>" target:app action:@selector(setWindowSize:) description:@"Set size of specific window"];
[argonaut add:@"size <width> <height> -w <id|name>" target:app action:@selector(setWindowSize:) description:@"Set size of specific window"];

[argonaut add:@"position" target:app action:@selector(printActiveWindowPosition:) description:@"Print position of active window"];
[argonaut add:@"position -w <id>" target:app action:@selector(printWindowPosition:) description:@"Print position of specific window"];
[argonaut add:@"position -w <id|name>" target:app action:@selector(printWindowPosition:) description:@"Print position of specific window"];
[argonaut add:@"position <x> <y>" target:app action:@selector(setActiveWindowPosition:) description:@"Set position of active window"];
[argonaut add:@"position <x> <y> -w <id>" target:app action:@selector(setWindowPosition:) description:@"Set position of specific window"];
[argonaut add:@"position <x> <y> -w <id|name>" target:app action:@selector(setWindowPosition:) description:@"Set position of specific window"];

[argonaut add:@"source" target:app action:@selector(printSourceFromActiveTab:) description:@"Print source from active tab"];
[argonaut add:@"source -t <id>" target:app action:@selector(printSourceFromTab:) description:@"Print source from specific tab"];
Expand Down
4 changes: 4 additions & 0 deletions scripts/chrome-beta-cli
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

export CHROME_BUNDLE_IDENTIFIER="com.google.Chrome.beta"
chrome-cli "$@"