Skip to content

Commit 41ad49c

Browse files
committed
🎉 Initial commit
0 parents  commit 41ad49c

12 files changed

+206
-0
lines changed

.editorconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
max_line_length = 80
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
logs
2+
*.log
3+
.DS_Store
4+
.vscode

.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"endOfLine": "lf",
3+
"singleQuote": false,
4+
"tabWidth": 2,
5+
"trailingComma": "all"
6+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Emmanuel Pilande
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div align="center">
2+
<h1>Alfred Browser Tabs 🔍</h1>
3+
</div>
4+
5+
<p align="center">
6+
<strong>Search browser tabs from Chrome, Brave, & Safari</strong>
7+
</p>
8+
9+
## Why?
10+
11+
You have hundreds of tabs open that you need to swift through and ain't nobody got time for that.
12+
13+
## Features
14+
15+
- 🏎 Blazing fast!
16+
- 💪 Supports Chrome, Brave, & Safari.
17+
- 🔍 Fuzzy search title & URLs.
18+
- ✨ Relevant results (last active window).
19+
- 🌶️ Customizable hotkeys & keywords.
20+
- 👯‍♀️ Copy URL.
21+
22+
## Installation
23+
24+
1. Download the Alfred Workflow.
25+
1. Double-click to import into Alfred (requires Powerpack).
26+
1. Review workflow, add hotkeys, customize.
27+
28+
## Usage
29+
30+
I would recommend setting up a hotkey to toggle the workflow to immediately search open tabs.
31+
32+
### Commands
33+
34+
- `chrome tabs {query}` - Fetch tabs from Google Chrome and filter based on query.
35+
- `brave tabs {query}` - Fetch tabs from Brave Browser and filter based on query.
36+
- `safari tabs {query}` - Fetch tabs from Safari and filter based on query.
37+
38+
### Copy to clipboard
39+
40+
Holding the `CTRL` key while selecting an item will copy the selected tab URL to your clipboard.
41+
42+
## License
43+
44+
[MIT License](https://oss.ninja/mit/epilande/)

src/assets/brave-icon.png

79.9 KB
Loading

src/assets/chrome-icon.png

34.3 KB
Loading

src/assets/safari-icon.png

186 KB
Loading

src/focus-tab-webkit.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env osascript -l JavaScript
2+
3+
function run(args) {
4+
let safari = Application("Safari");
5+
let query = args[0];
6+
let [windowIndex, url] = query.split(",");
7+
8+
function getTab() {
9+
let result;
10+
let window = safari.windows[windowIndex];
11+
if (window) {
12+
for (let index in window.tabs) {
13+
let tab = window.tabs[index];
14+
let tabURL = tab.url() || tab.name();
15+
if (tabURL && tabURL.startsWith(url)) {
16+
result = tab;
17+
break;
18+
}
19+
}
20+
return result;
21+
}
22+
}
23+
24+
let tab = getTab();
25+
safari.activate();
26+
safari.windows[windowIndex].currentTab = tab;
27+
}

src/focus-tab.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env osascript -l JavaScript
2+
3+
function run(args) {
4+
ObjC.import("stdlib");
5+
let browser = $.getenv("browser");
6+
let chrome = Application(browser);
7+
let query = args[0];
8+
let [arg1, arg2] = query.split(",");
9+
let windowIndex = parseInt(arg1);
10+
let tabIndex = parseInt(arg2);
11+
12+
chrome.activate();
13+
chrome.windows[windowIndex].visible = true;
14+
chrome.windows[windowIndex].activeTabIndex = tabIndex + 1;
15+
chrome.windows[windowIndex].index = 1;
16+
}

src/list-tabs-webkit.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env osascript -l JavaScript
2+
3+
function run(args) {
4+
let browser = args[0];
5+
let chrome = Application(browser);
6+
chrome.includeStandardAdditions = true;
7+
let windowCount = chrome.windows.length;
8+
let tabsTitle = chrome.windows.tabs.name();
9+
let tabsUrl = chrome.windows.tabs.url();
10+
let tabsMap = {};
11+
12+
for (let w = 0; w < windowCount; w++) {
13+
for (let t = 0; t < tabsTitle[w].length; t++) {
14+
let url = tabsUrl[w][t] || "";
15+
let matchUrl = url.replace(/(^\w+:|^)\/\//, "");
16+
let title = tabsTitle[w][t] || matchUrl;
17+
18+
tabsMap[url] = {
19+
title,
20+
url,
21+
subtitle: url,
22+
windowIndex: w,
23+
tabIndex: t,
24+
arg: `${w},${url || title}`,
25+
match: `${title} ${matchUrl}`,
26+
};
27+
}
28+
}
29+
30+
let items = Object.keys(tabsMap).reduce((acc, url) => {
31+
acc.push(tabsMap[url]);
32+
return acc;
33+
}, []);
34+
35+
return JSON.stringify({ items });
36+
}

src/list-tabs.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env osascript -l JavaScript
2+
3+
function run(args) {
4+
let browser = args[0];
5+
let chrome = Application(browser);
6+
chrome.includeStandardAdditions = true;
7+
let windowCount = chrome.windows.length;
8+
let tabsTitle =
9+
browser === "Safari"
10+
? chrome.windows.tabs.name()
11+
: chrome.windows.tabs.title();
12+
let tabsUrl = chrome.windows.tabs.url();
13+
let tabsMap = {};
14+
15+
for (let w = 0; w < windowCount; w++) {
16+
for (let t = 0; t < tabsTitle[w].length; t++) {
17+
let url = tabsUrl[w][t] || "";
18+
let matchUrl = url.replace(/(^\w+:|^)\/\//, "");
19+
let title = tabsTitle[w][t] || matchUrl;
20+
21+
tabsMap[url] = {
22+
title,
23+
url,
24+
subtitle: url,
25+
windowIndex: w,
26+
tabIndex: t,
27+
arg: `${w},${t},${url}`,
28+
match: `${title} ${matchUrl}`,
29+
};
30+
}
31+
}
32+
33+
let items = Object.keys(tabsMap).reduce((acc, url) => {
34+
acc.push(tabsMap[url]);
35+
return acc;
36+
}, []);
37+
38+
return JSON.stringify({ items });
39+
}

0 commit comments

Comments
 (0)