Skip to content

Commit f4165e1

Browse files
committed
Support icon uploads in Firefox again
1 parent 28ee8ee commit f4165e1

File tree

10 files changed

+144
-17
lines changed

10 files changed

+144
-17
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 4.2.0
4+
5+
- Port to TypeScript
6+
- Make it work in Firefox again, including custom icons
7+
- Various bugfixes
8+
39
## 4.1.2
410

511
- Replace Google Image Search with Google Lens because Google removed the old reverse search. This will also apply to current users.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "reverse-image-search",
33
"description": "Reverse Image Search",
4-
"version": "4.1.2",
4+
"version": "4.2.0",
55
"author": "Brawl345",
66
"license": "Unlicense",
77
"repository": {
@@ -12,8 +12,8 @@
1212
"scripts": {
1313
"dev": "tsx build.ts",
1414
"build": "cross-env NODE_ENV=\"production\" tsx build.ts",
15-
"start:chrome": "web-ext run -t chromium",
16-
"start:firefox": "web-ext run -t firefox-desktop",
15+
"start:chrome": "web-ext run --no-reload -t chromium",
16+
"start:firefox": "web-ext run --no-reload -t firefox-desktop",
1717
"lint:types": "tsc --project tsconfig.json",
1818
"lint:code": "biome lint",
1919
"lint:web-ext": "npm run build && web-ext lint",

public/_locales/de/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
"msgIconUploadNotSquareImage": {
3030
"message": "Bitte lade ein quadratisches Bild hoch!"
3131
},
32+
"msgIconUploadNotSupported": {
33+
"message": "Die Verwendung von Icons in Kontextmenüs wird von diesem Browser nicht unterstützt."
34+
},
3235
"msgSuccessSaveOptions": {
3336
"message": "Gespeichert!"
3437
},

public/_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
"msgIconUploadNotSquareImage": {
3030
"message": "Please upload square image!"
3131
},
32+
"msgIconUploadNotSupported": {
33+
"message": "Using icons in context menus is not supported by this browser."
34+
},
3235
"msgSuccessSaveOptions": {
3336
"message": "Saved!"
3437
},

public/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"name": "__MSG_extensionName__",
55
"description": "__MSG_extensionDescription__",
66
"author": "Andreas Bielawski",
7-
"version": "4.1.2",
7+
"version": "4.2.0",
88
"icons": {
99
"16": "icons/16.png",
1010
"32": "icons/32.png",
@@ -33,7 +33,7 @@
3333
"page": "options/options.html"
3434
},
3535
"content_security_policy": {
36-
"extension_pages": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
36+
"extension_pages": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:"
3737
},
3838
"permissions": ["contextMenus", "storage"],
3939
"browser_specific_settings": {

source/options/components/Form.svelte

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,6 @@
187187

188188
<hr />
189189

190-
<div class="row">
191-
<div class="col">
192-
<button class="btn btn-success" type="submit"
193-
>{getMessage('saveOptions')}</button
194-
>
195-
</div>
196-
</div>
197-
198190
{#if $alert.variant !== null}
199191
<div class="row mt-3" transition:fly>
200192
<div class="col">
@@ -215,4 +207,12 @@
215207
</div>
216208
</div>
217209
{/if}
210+
211+
<div class="row">
212+
<div class="col">
213+
<button class="btn btn-success" type="submit"
214+
>{getMessage('saveOptions')}</button
215+
>
216+
</div>
217+
</div>
218218
</form>

source/options/components/Provider.svelte

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,81 @@
11
<script lang="ts">
22
import { fly } from 'svelte/transition';
3-
import type { StorageProvider } from "../../types";
4-
import { getMessage } from '../../utils';
3+
import type { StorageProvider } from '../../types';
4+
import { base64EncodeIcon, getMessage, isFirefox } from "../../utils";
55
import { options } from '../stores/options-store';
66
77
export let index: number;
88
export let provider: StorageProvider;
9+
10+
let errorMsg: string | null = null;
11+
12+
const hideAlert = () => {
13+
errorMsg = null;
14+
}
15+
16+
const getIcon = (icon: string) => {
17+
if (icon.startsWith('icons/')) {
18+
return chrome.runtime.getURL(icon);
19+
}
20+
return icon;
21+
}
22+
23+
$: iconUrl = getIcon(provider.icon);
24+
25+
const uploadIcon = () => {
26+
if (isFirefox) {
27+
errorMsg = getMessage("msgIconUploadNotSupported");
28+
return;
29+
}
30+
31+
const fileInput = document.createElement('input');
32+
fileInput.type = 'file';
33+
fileInput.accept = 'image/*';
34+
35+
fileInput.addEventListener('change', () => {
36+
if (!fileInput.files || fileInput.files.length === 0) {
37+
return;
38+
}
39+
errorMsg = null;
40+
41+
const file = fileInput.files[0];
42+
if (!file.type.startsWith('image/')) {
43+
errorMsg = getMessage('msgIconUploadNotImage');
44+
return;
45+
}
46+
47+
const tmpImg = new Image();
48+
49+
tmpImg.addEventListener('error', () => {
50+
errorMsg = getMessage('msgIconUploadNotImage');
51+
});
52+
53+
tmpImg.addEventListener('load', () => {
54+
if (tmpImg.naturalHeight !== tmpImg.naturalWidth) {
55+
errorMsg = getMessage('msgIconUploadNotSquareImage');
56+
return;
57+
}
58+
59+
const canvas = document.createElement('canvas');
60+
canvas.width = 24;
61+
canvas.height = 24;
62+
63+
const ctx = canvas.getContext('2d');
64+
if (!ctx) {
65+
errorMsg = getMessage('msgIconUploadNotImage');
66+
return;
67+
}
68+
ctx.drawImage(tmpImg, 0, 0, 24, 24);
69+
70+
provider.icon = base64EncodeIcon(ctx);
71+
});
72+
73+
tmpImg.src = URL.createObjectURL(file);
74+
});
75+
76+
fileInput.click();
77+
}
78+
979
</script>
1080

1181

@@ -31,7 +101,7 @@
31101
/>
32102
</div>
33103
<div class="input-group-text">
34-
<img alt="Icon" width="24" height="24" src={`../${provider.icon}`} />
104+
<img class="pointer" alt="Icon" width="24" height="24" src={iconUrl} on:click={uploadIcon} />
35105
</div>
36106
<input
37107
class="form-control"
@@ -59,3 +129,24 @@
59129
>❌
60130
</button>
61131
</fieldset>
132+
133+
{#if errorMsg !== null}
134+
<div class="row mt-3" transition:fly>
135+
<div class="col">
136+
<div
137+
class="alert alert-danger alert-dismissible"
138+
role="alert"
139+
>
140+
{errorMsg}
141+
<button
142+
type="button"
143+
class="btn-close"
144+
data-bs-dismiss="alert"
145+
aria-label="Close"
146+
on:click={hideAlert}
147+
>&nbsp;
148+
</button>
149+
</div>
150+
</div>
151+
</div>
152+
{/if}

source/options/options.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ $enable-gradients: true;
2121
@import "bootstrap/scss/badge";
2222
@import "bootstrap/scss/close";
2323
@import "bootstrap/scss/tooltip";
24+
25+
.pointer {
26+
cursor: pointer;
27+
}

source/service-worker/service-worker.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { onReverseSearch, setupContextMenu } from './service-worker-functions';
22

3-
chrome.runtime.onStartup.addListener(setupContextMenu);
43
chrome.runtime.onInstalled.addListener(async (details) => {
54
await setupContextMenu();
65

source/utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,24 @@ export const getLowerIndex = (length: number, index: number) => {
3131
}
3232
return index + 1;
3333
};
34+
35+
export const base64EncodeIcon = (ctx: CanvasRenderingContext2D): string => {
36+
const pngBase64 = ctx.canvas.toDataURL();
37+
const { data: pixels } = ctx.getImageData(0, 0, 24, 24);
38+
39+
const isTransparent = pixels.some(
40+
(value, index) => index % 4 === 3 && value !== 255,
41+
);
42+
if (isTransparent) {
43+
return pngBase64;
44+
}
45+
46+
const jpegBase64 = ctx.canvas.toDataURL('image/jpeg', 0.8);
47+
if (pngBase64.length < jpegBase64.length) {
48+
return pngBase64;
49+
}
50+
return jpegBase64;
51+
};
52+
53+
// @ts-ignore
54+
export const isFirefox = window.browser && browser.runtime;

0 commit comments

Comments
 (0)