Skip to content

Commit 74d1400

Browse files
committed
Initial release.
0 parents  commit 74d1400

37 files changed

+330
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
dist

LICENSE.txt

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) 2022 Nate Hill
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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# ★ Omnibookmarks
2+
3+
The [Omnibookmarks browser extension](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj) is **the fastest way to open bookmarks**. Just type a keyword into the address bar to quickly open or add a bookmark:
4+
5+
![opening a bookmark](readme-assets/open.gif)
6+
7+
Get Omnibookmarks from the [Chrome Web Store](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj) or the [FireFox Add-Ons](https://addons.mozilla.org/en-US/firefox/addon/omnibookmarks) site.
8+
9+
## Benefits
10+
11+
⚡️ Lightning fast
12+
13+
📖 Saves to the browser's built-in bookmarks folder
14+
15+
🔐 Open source, with [simple source code](background.js) that is easy to audit and trust
16+
17+
18+
## Usage
19+
20+
First, activate Omnibookmarks by typing the letter "b" into the address bar, followed by a space:
21+
22+
<p align="center">
23+
<img alt="activating Omnibookmarks" src="readme-assets/activate.gif" width="640">
24+
</p>
25+
26+
### Adding a bookmark
27+
28+
To add a bookmark for the current page, type "+" followed by the keyword you want to assign it, and press Enter:
29+
30+
<p align="center">
31+
<img alt="adding a bookmark" src="readme-assets/add.gif" width="640">
32+
</p>
33+
34+
### Opening a bookmark
35+
36+
To open a bookmark you previously added, type its keyword and press Enter:
37+
38+
<p align="center">
39+
<img alt="opening a bookmark" src="readme-assets/open.gif" width="640">
40+
</p>
41+
42+
### Removing a bookmark
43+
44+
To remove a bookmark you previously added, type "-" followed by its keyword, and press Enter:
45+
46+
<p align="center">
47+
<img alt="removing a bookmark" src="readme-assets/remove.gif" width="640">
48+
</p>
49+
50+
### Viewing all bookmarks
51+
52+
Omnibookmarks saves pages to a bookmarks folder named "Omnibookmarks". This folder is placed in "Other Bookmarks" by default, but you can move it to a different location if you want to.
53+
54+
## Supported browsers
55+
56+
Omnibookmarks supports most desktop browsers:
57+
58+
- [Chrome](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj)
59+
- [FireFox](https://addons.mozilla.org/en-US/firefox/addon/omnibookmarks)
60+
- [Edge](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj) (v79+)
61+
- [Opera](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj)
62+
- [Brave](https://chrome.google.com/webstore/detail/omnibookmarks/ahacopipkbdhmgjlkbnkpoafeakjjkkj)
63+
64+
Omnibookmarks doesn't support the following browsers:
65+
66+
- Safari (doesn't support [omnibox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox))
67+
- Mobile browsers
68+
- Legacy Edge (v18 and older)
69+
70+
## Development
71+
72+
To create a release for Chromium-based browsers (Chrome, Edge, Opera):
73+
74+
```sh
75+
# Creates a release at dist/omnibookmarks-chromium
76+
./scripts/package-chromium.sh
77+
```
78+
79+
To create a release for FireFox:
80+
81+
```sh
82+
# Creates a release at dist/omnibookmarks-firefox
83+
./scripts/package-firefox.sh
84+
```
85+
86+
## Inspiration
87+
88+
Omnibookmarks is heavily inspired by Hashmem, an extension that is no longer available. Hashmem provided the same approach for saving and opening URLs, but it had several significant drawbacks:
89+
90+
- Removed from the Chrome Web Store for an unnamed policy violation and was also unavailable for FireFox.
91+
- Required granting the extension permission to send HTTP requests to several URLs controlled by the author.
92+
- Not open source, and its JavaScript files were obfuscated.
93+
- Saved bookmarks using the extension storage API instead of saving them to the browser's built-in bookmarks database.
94+
95+
So, Omnibookmarks seeks to provide a replacement for Hashmem without these drawbacks.

background.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Polyfill globalThis.browser instead of using globalThis.chrome because FireFox's
2+
// globalThis.chrome APIs don't yet support the promise-based APIs from Manifest v3.
3+
// Also, use globalThis because window isn't defined in service workers.
4+
if (!globalThis.browser) {
5+
globalThis.browser = chrome;
6+
}
7+
8+
browser.omnibox.onInputEntered.addListener(handleOmniboxInput);
9+
10+
function handleOmniboxInput(rawText) {
11+
12+
const text = rawText.trim();
13+
if (!text.length) {
14+
return;
15+
}
16+
const prefix = text[0];
17+
switch (prefix) {
18+
case '+':
19+
saveBookmark(text);
20+
break;
21+
case '-':
22+
removeBookmark(text);
23+
break;
24+
default:
25+
openBookmark(text);
26+
}
27+
}
28+
29+
async function saveBookmark(text) {
30+
31+
const name = text.substr(1);
32+
if (!name) {
33+
notifyNameMissing('add', '+');
34+
return;
35+
}
36+
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
37+
const { url } = tabs[0];
38+
const newBookmarkCreated = await Bookmarks.save(name, url);
39+
const verb = newBookmarkCreated ? 'added' : 'updated';
40+
notify(`Bookmark ${verb}`, `Successfully ${verb} the bookmark "${name}".`);
41+
}
42+
43+
async function removeBookmark(text) {
44+
45+
const name = text.substr(1);
46+
if (!name) {
47+
notifyNameMissing('remove', '-');
48+
return;
49+
}
50+
const matchingBookmarkRemoved = await Bookmarks.remove(name);
51+
if (matchingBookmarkRemoved) {
52+
notify('Bookmark removed', `Successfully removed the bookmark "${name}".`);
53+
return;
54+
}
55+
notifyNotFound(name);
56+
}
57+
58+
async function openBookmark(name) {
59+
60+
const url = await Bookmarks.get(name);
61+
if (url) {
62+
browser.tabs.create({ url });
63+
return;
64+
}
65+
notifyNotFound(name);
66+
}
67+
68+
function notify(title, message) {
69+
70+
browser.notifications.create('', {
71+
type: 'basic',
72+
title,
73+
message,
74+
iconUrl: 'img/icon_48.png'
75+
});
76+
}
77+
78+
function notifyNameMissing(verb, prefix) {
79+
80+
notify('You forgot to include a bookmark name!', `To ${verb} a bookmark, include a name after the "${prefix}", like "${prefix}fav".`);
81+
}
82+
83+
function notifyNotFound(name) {
84+
85+
notify('Bookmark not found', `You don't have any bookmarks with the name "${name}" yet.`)
86+
}
87+
88+
class Bookmarks {
89+
90+
static async remove(title) {
91+
92+
const bookmark = await this._getBookmark(title);
93+
if (bookmark) {
94+
await browser.bookmarks.remove(bookmark.id);
95+
return true;
96+
}
97+
return false;
98+
}
99+
100+
/** @returns {string|null} The URL for the matching bookmark, or null if none matches. */
101+
static async get(title) {
102+
103+
const bookmark = await this._getBookmark(title);
104+
return bookmark && bookmark.url;
105+
}
106+
107+
/** @returns {boolean} true if a new bookmark was created or false if an existing one was updated. */
108+
static async save(title, url) {
109+
110+
const existingBookmark = await this._getBookmark(title);
111+
if (existingBookmark) {
112+
await browser.bookmarks.update(existingBookmark.id, { url });
113+
return false;
114+
}
115+
const parentId = await this._getFolderId();
116+
await browser.bookmarks.create({ parentId, title, url });
117+
return true;
118+
}
119+
120+
static _cachedFolderId = 0;
121+
static FOLDER_NAME = 'Omnibookmarks';
122+
123+
static async _getBookmark(title) {
124+
125+
const bookmarks = await this._getChildren();
126+
const bookmarkOrNull = bookmarks.find(b => b.title === title) || null;
127+
return bookmarkOrNull;
128+
}
129+
130+
static async _getChildren() {
131+
132+
const id = await this._getFolderId();
133+
const children = await browser.bookmarks.getChildren(id);
134+
return children;
135+
}
136+
137+
static async _getFolderId() {
138+
139+
if (!this._cachedFolderId) {
140+
// Search to allow the Omnibookmarks folder to be manually moved somewhere else.
141+
const results = await browser.bookmarks.search({ title: this.FOLDER_NAME })
142+
const existingFolder = results[0];
143+
if (existingFolder) {
144+
this._cachedFolderId = existingFolder.id;
145+
} else {
146+
const newFolder = await browser.bookmarks.create({ title: this.FOLDER_NAME });
147+
this._cachedFolderId = newFolder.id;
148+
}
149+
}
150+
return this._cachedFolderId;
151+
}
152+
}
24.8 KB
Loading
Binary file not shown.
Binary file not shown.
Binary file not shown.
140 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)