Language changes
This commit is contained in:
parent
b7ff7d1ada
commit
74fe885fd0
14 changed files with 288 additions and 182 deletions
|
@ -1 +1,5 @@
|
||||||
{ "useTabs": true, "semi": true }
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"semi": true,
|
||||||
|
"svelteSortOrder": "scripts-markup-styles"
|
||||||
|
}
|
||||||
|
|
|
@ -7,14 +7,35 @@ import content_script from "../frontend/content_script/content_script.ts?file";
|
||||||
let com = new Communicator();
|
let com = new Communicator();
|
||||||
|
|
||||||
const scraper = new GTranslateScraper();
|
const scraper = new GTranslateScraper();
|
||||||
com.translateCallback = (toTranslate, sender) =>
|
com.translateCallback = async (toTranslate, sender) =>
|
||||||
scraper.translate(toTranslate, sender.tab?.id);
|
scraper.translate(toTranslate, await getCurrentLanguages(), sender.tab?.id);
|
||||||
|
|
||||||
com.getLanguagesCallback = () => scraper.languages;
|
com.getLanguagesCallback = () => scraper.languages;
|
||||||
|
|
||||||
const db = new Flashcards();
|
const db = new Flashcards();
|
||||||
com.addFlashcardCallback = (c) => db.addFlashcard(c);
|
com.addFlashcardCallback = (c) => db.addFlashcard(c);
|
||||||
com.removeFlashcardCallback = (c) => db.removeFlashcard(c);
|
com.removeFlashcardCallback = (c) => db.removeFlashcard(c);
|
||||||
|
|
||||||
|
const getCurrentLanguages = async () => {
|
||||||
|
const langs = await scraper.languages;
|
||||||
|
let srcLangCode =
|
||||||
|
(await browser.storage.local.get("srcLang"))["srcLang"] ?? "de";
|
||||||
|
let dstLangCode =
|
||||||
|
(await browser.storage.local.get("dstLang"))["dstLang"] ?? "en";
|
||||||
|
|
||||||
|
const srcLang = langs.filter((l) => l.code == srcLangCode).pop()!;
|
||||||
|
const dstLang = langs.filter((l) => l.code == dstLangCode).pop()!;
|
||||||
|
return { srcLang: srcLang, dstLang: dstLang };
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentLanguages = async (l: Partial<LanguagePair>) => {
|
||||||
|
if (l.srcLang) await browser.storage.local.set({ srcLang: l.srcLang.code });
|
||||||
|
if (l.dstLang) await browser.storage.local.set({ dstLang: l.dstLang.code });
|
||||||
|
};
|
||||||
|
|
||||||
|
com.getCurrentLanguagesCallback = getCurrentLanguages;
|
||||||
|
com.setCurrentLanguagesCallback = setCurrentLanguages;
|
||||||
|
|
||||||
const getTabID = async (s: Runtime.MessageSender): Promise<number> => {
|
const getTabID = async (s: Runtime.MessageSender): Promise<number> => {
|
||||||
if (s.tab?.id) return s.tab.id;
|
if (s.tab?.id) return s.tab.id;
|
||||||
let tabs = await browser.tabs.query({
|
let tabs = await browser.tabs.query({
|
||||||
|
|
|
@ -1,29 +1,33 @@
|
||||||
|
import { newPromise } from "../utils";
|
||||||
|
|
||||||
export class Flashcards {
|
export class Flashcards {
|
||||||
db!: IDBDatabase;
|
db: Promise<IDBDatabase>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
let [promise, resolve, reject] = newPromise<IDBDatabase>();
|
||||||
|
this.db = promise;
|
||||||
|
|
||||||
const req = indexedDB.open("FlashCardDB", 1);
|
const req = indexedDB.open("FlashCardDB", 1);
|
||||||
req.onupgradeneeded = () => {
|
req.onupgradeneeded = () => {
|
||||||
let objectStore = req.result.createObjectStore("flashcards", {
|
let objectStore = req.result.createObjectStore("flashcards", {
|
||||||
keyPath: "id",
|
keyPath: "id",
|
||||||
autoIncrement: true,
|
autoIncrement: true,
|
||||||
});
|
});
|
||||||
objectStore.createIndex("src", "src", { unique: true });
|
objectStore.createIndex("src", "src");
|
||||||
objectStore.createIndex("result", "result");
|
objectStore.createIndex("result", "result");
|
||||||
objectStore.createIndex("dateAdded", "dateAdded");
|
objectStore.createIndex("dateAdded", "dateAdded");
|
||||||
objectStore.createIndex("exported", "exported");
|
objectStore.createIndex("exported", "exported");
|
||||||
|
objectStore.createIndex("srcLang", "languages.srcLang.code");
|
||||||
|
objectStore.createIndex("dstLang", "languages.dstLang.code");
|
||||||
};
|
};
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => resolve(req.result);
|
||||||
this.db = req.result;
|
req.onerror = () =>
|
||||||
};
|
reject(
|
||||||
req.onerror = () => {
|
|
||||||
throw new Error(
|
|
||||||
`Error initializing the database backend, ${req.error}. This shouldn't ever happen`
|
`Error initializing the database backend, ${req.error}. This shouldn't ever happen`
|
||||||
);
|
);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addFlashcard(t: Translation | Flashcard): Promise<Flashcard> {
|
async addFlashcard(t: Translation | Flashcard): Promise<Flashcard> {
|
||||||
let card: Flashcard;
|
let card: Flashcard;
|
||||||
if ("dateAdded" in t && "exported" in t) card = t;
|
if ("dateAdded" in t && "exported" in t) card = t;
|
||||||
else {
|
else {
|
||||||
|
@ -34,41 +38,41 @@ export class Flashcards {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = this.db
|
let req = (await this.db)
|
||||||
.transaction(["flashcards"], "readwrite")
|
.transaction(["flashcards"], "readwrite")
|
||||||
.objectStore("flashcards")
|
.objectStore("flashcards")
|
||||||
.add(card);
|
.add(card);
|
||||||
return new Promise((resolve, reject) => {
|
let [promise, resolve, reject] = newPromise<Flashcard>();
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
card.id = req.result;
|
card.id = req.result;
|
||||||
resolve(card);
|
resolve(card);
|
||||||
};
|
};
|
||||||
req.onerror = () => reject(req.error);
|
req.onerror = () => reject(req.error);
|
||||||
});
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFlashcard(card: Flashcard): Promise<void> {
|
async removeFlashcard(card: Flashcard): Promise<void> {
|
||||||
if (card.id) {
|
if (!card.id) return Promise.reject("Undefined ID");
|
||||||
let req = this.db
|
let req = (await this.db)
|
||||||
.transaction(["flashcards"], "readwrite")
|
.transaction(["flashcards"], "readwrite")
|
||||||
.objectStore("flashcards")
|
.objectStore("flashcards")
|
||||||
.delete(card.id);
|
.delete(card.id);
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
let [promise, resolve, reject] = newPromise<void>();
|
||||||
req.onsuccess = () => resolve();
|
req.onsuccess = () => resolve();
|
||||||
req.onerror = () => reject(req.error);
|
req.onerror = () => reject(req.error);
|
||||||
});
|
return promise;
|
||||||
} else return Promise.reject("Undefined ID");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllCards(): Promise<Array<Flashcard>> {
|
async getAllCards(): Promise<Array<Flashcard>> {
|
||||||
let req = this.db
|
let req = (await this.db)
|
||||||
.transaction(["flashcards"], "readonly")
|
.transaction(["flashcards"], "readonly")
|
||||||
.objectStore("flashcards")
|
.objectStore("flashcards")
|
||||||
.getAll();
|
.getAll();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
let [promise, resolve, reject] = newPromise<Flashcard[]>();
|
||||||
req.onsuccess = () => resolve(req.result);
|
req.onsuccess = () => resolve(req.result);
|
||||||
req.onerror = () => reject(req.error);
|
req.onerror = () => reject(req.error);
|
||||||
});
|
return promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ export enum MessageKinds {
|
||||||
translationFinished = "translation",
|
translationFinished = "translation",
|
||||||
languageList = "languages",
|
languageList = "languages",
|
||||||
}
|
}
|
||||||
interface TranslationMessage {
|
export interface TranslationMessage {
|
||||||
messageKind: MessageKinds.translationFinished;
|
messageKind: MessageKinds.translationFinished;
|
||||||
translation: Translation;
|
translation: Translation;
|
||||||
}
|
}
|
||||||
interface LanguageListMessage {
|
export interface LanguageListMessage {
|
||||||
messageKind: MessageKinds.languageList;
|
messageKind: MessageKinds.languageList;
|
||||||
languages: Array<Language>;
|
languages: Array<Language>;
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,17 @@ export type Message = TranslationMessage | LanguageListMessage;
|
||||||
const sendMessage = (m: Message) => conn.postMessage(m);
|
const sendMessage = (m: Message) => conn.postMessage(m);
|
||||||
|
|
||||||
const src = <HTMLTextAreaElement>document.querySelector("textarea#source");
|
const src = <HTMLTextAreaElement>document.querySelector("textarea#source");
|
||||||
|
|
||||||
const result = <HTMLDivElement>(
|
const result = <HTMLDivElement>(
|
||||||
document.querySelector("div.results-container")
|
document.querySelector("div.results-container")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [srcLang, dstLang] = Array.from(
|
||||||
|
document.querySelectorAll(".jfk-button-checked")
|
||||||
|
).map((d) => ({
|
||||||
|
code: d.getAttribute("value")!,
|
||||||
|
name: d.textContent!,
|
||||||
|
}));
|
||||||
|
|
||||||
const isTranslating = () => result.classList.contains("translating");
|
const isTranslating = () => result.classList.contains("translating");
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
@ -50,7 +56,14 @@ export type Message = TranslationMessage | LanguageListMessage;
|
||||||
const sendTranslation = () => {
|
const sendTranslation = () => {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
messageKind: MessageKinds.translationFinished,
|
messageKind: MessageKinds.translationFinished,
|
||||||
translation: { src: src.value, result: result.innerText.trim() },
|
translation: {
|
||||||
|
src: src.value,
|
||||||
|
result: result.innerText.trim(),
|
||||||
|
languages: {
|
||||||
|
srcLang: srcLang,
|
||||||
|
dstLang: dstLang,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
import { browser, WebRequest, Runtime } from "webextension-polyfill-ts";
|
import { browser, WebRequest, Runtime } from "webextension-polyfill-ts";
|
||||||
import content_script from "./gtranslate_content_script.ts?file";
|
import content_script from "./gtranslate_content_script.ts?file";
|
||||||
import { Message, MessageKinds } from "./gtranslate_content_script";
|
import { Message, MessageKinds } from "./gtranslate_content_script";
|
||||||
|
import { newPromise } from "../utils";
|
||||||
|
|
||||||
interface TranslationRequest {
|
interface TranslationRequest {
|
||||||
tabID?: number;
|
tabID?: number;
|
||||||
resolveFn: (value: Translation) => void;
|
resolveFn: (value: Translation) => void;
|
||||||
rejectFn: (reason: String) => void;
|
rejectFn: (reason: String) => void;
|
||||||
toTranslate: string;
|
toTranslate: string;
|
||||||
|
lang: LanguagePair;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GTranslateScraper {
|
export class GTranslateScraper {
|
||||||
private conn?: Runtime.Port;
|
private conn?: Runtime.Port;
|
||||||
private current?: TranslationRequest;
|
|
||||||
private queue: Array<TranslationRequest> = [];
|
private queue: Array<TranslationRequest> = [];
|
||||||
|
private iframe = <HTMLIFrameElement>document.createElement("iframe");
|
||||||
languages: Promise<Array<Language>>;
|
languages: Promise<Array<Language>>;
|
||||||
|
lang: LanguagePair | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let languagesResolve: (value: Array<Language>) => void;
|
let languagesResolve: (value: Array<Language>) => void;
|
||||||
this.languages = new Promise((resolv) => (languagesResolve = resolv));
|
[this.languages, languagesResolve] = newPromise<Array<Language>>();
|
||||||
|
|
||||||
browser.runtime.onConnect.addListener((p) => {
|
browser.runtime.onConnect.addListener((p) => {
|
||||||
if (p.name != "gtranslate_scraper_conn") return;
|
if (p.name != "gtranslate_scraper_conn") return;
|
||||||
|
@ -33,6 +36,30 @@ export class GTranslateScraper {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
this.loadIframe("de", "en"); //Sample languages so we can get the language list
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadIframe(srcLangCode: string, dstLangCode: string) {
|
||||||
|
if (!document.body.contains(this.iframe))
|
||||||
|
document.body.appendChild(this.iframe);
|
||||||
|
this.conn = undefined;
|
||||||
|
|
||||||
|
//Registers a temp content script, because we cannot inject scripts into iframes created on the background html page, because they have no tabId
|
||||||
|
browser.contentScripts
|
||||||
|
.register({
|
||||||
|
matches: ["<all_urls>"],
|
||||||
|
allFrames: true,
|
||||||
|
js: [
|
||||||
|
{
|
||||||
|
file: content_script,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
runAt: "document_end",
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
this.iframe.addEventListener("load", r.unregister, { once: true });
|
||||||
|
this.iframe.src = `https://translate.google.com/?op=translate&sl=${srcLangCode}&tl=${dstLangCode}`;
|
||||||
|
});
|
||||||
|
|
||||||
//This allows us to create the google translate iframe by removing the x-frame-options header
|
//This allows us to create the google translate iframe by removing the x-frame-options header
|
||||||
browser.webRequest.onHeadersReceived.addListener(
|
browser.webRequest.onHeadersReceived.addListener(
|
||||||
|
@ -43,57 +70,47 @@ export class GTranslateScraper {
|
||||||
},
|
},
|
||||||
["blocking", "responseHeaders"]
|
["blocking", "responseHeaders"]
|
||||||
);
|
);
|
||||||
let iframe = <HTMLIFrameElement>document.createElement("iframe");
|
|
||||||
|
|
||||||
//TODO make the language customizable
|
|
||||||
iframe.src = "https://translate.google.com/?op=translate&sl=de&tl=en";
|
|
||||||
|
|
||||||
//Registers a temp content script, because we cannot inject scripts into iframes created on the background html page, because they have no tabId
|
|
||||||
const js = {
|
|
||||||
file: content_script,
|
|
||||||
};
|
|
||||||
browser.contentScripts
|
|
||||||
.register({
|
|
||||||
matches: ["<all_urls>"],
|
|
||||||
allFrames: true,
|
|
||||||
js: [js],
|
|
||||||
runAt: "document_end",
|
|
||||||
})
|
|
||||||
.then((r) => {
|
|
||||||
iframe.addEventListener("load", r.unregister);
|
|
||||||
document.body.appendChild(iframe);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
private onTranslationRecieved(message: Translation) {
|
|
||||||
if (this.current) this.current.resolveFn(message);
|
private onTranslationRecieved(trans: Translation) {
|
||||||
this.current = undefined;
|
const current = this.queue.pop();
|
||||||
|
if (current) current.resolveFn(trans);
|
||||||
this.processFromQueue(this.conn!);
|
this.processFromQueue(this.conn!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private processTranslationRequest(
|
|
||||||
request: TranslationRequest,
|
|
||||||
conn: Runtime.Port
|
|
||||||
) {
|
|
||||||
this.current = request;
|
|
||||||
conn.postMessage(request.toTranslate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private processFromQueue(conn: Runtime.Port) {
|
private processFromQueue(conn: Runtime.Port) {
|
||||||
const next = this.queue.pop();
|
const request = this.queue.slice(-1).pop();
|
||||||
if (next) this.processTranslationRequest(next, conn);
|
if (!request) return;
|
||||||
|
|
||||||
|
if (this.lang != request.lang) {
|
||||||
|
this.lang = request.lang;
|
||||||
|
|
||||||
|
this.loadIframe(this.lang.srcLang.code, this.lang.dstLang.code);
|
||||||
|
} else conn.postMessage(request.toTranslate);
|
||||||
}
|
}
|
||||||
|
|
||||||
translate(to_translate: string, tabID?: number): Promise<Translation> {
|
translate(
|
||||||
if (to_translate == "") return Promise.resolve({ src: "", result: "" });
|
toTranslate: string,
|
||||||
|
langs: LanguagePair,
|
||||||
|
tabID?: number
|
||||||
|
): Promise<Translation> {
|
||||||
|
toTranslate = toTranslate.trim();
|
||||||
|
if (toTranslate == "")
|
||||||
|
return Promise.resolve({
|
||||||
|
src: "",
|
||||||
|
result: "",
|
||||||
|
languages: langs,
|
||||||
|
});
|
||||||
|
|
||||||
|
let [promise, resolve, reject] = newPromise<Translation>();
|
||||||
|
|
||||||
const p = new Promise((resolve: (value: Translation) => void, reject) => {
|
|
||||||
const req = {
|
const req = {
|
||||||
toTranslate: to_translate,
|
toTranslate: toTranslate,
|
||||||
resolveFn: resolve,
|
resolveFn: resolve,
|
||||||
rejectFn: reject,
|
rejectFn: reject,
|
||||||
tabID: tabID,
|
tabID: tabID,
|
||||||
|
lang: langs,
|
||||||
};
|
};
|
||||||
if (this.current || !this.conn) {
|
|
||||||
//Remove the requests from the same tab
|
//Remove the requests from the same tab
|
||||||
if (tabID)
|
if (tabID)
|
||||||
this.queue = this.queue.filter((r) => {
|
this.queue = this.queue.filter((r) => {
|
||||||
|
@ -103,11 +120,9 @@ export class GTranslateScraper {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.queue.push(req);
|
this.queue.push(req);
|
||||||
} else {
|
if (this.queue.length == 1 && this.conn) this.processFromQueue(this.conn);
|
||||||
this.processTranslationRequest(req, this.conn);
|
|
||||||
}
|
return promise;
|
||||||
});
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,17 @@ export class Communicator {
|
||||||
getLanguagesCallback?: (
|
getLanguagesCallback?: (
|
||||||
sender: Runtime.MessageSender
|
sender: Runtime.MessageSender
|
||||||
) => Promise<Array<Language>>;
|
) => Promise<Array<Language>>;
|
||||||
|
getCurrentLanguagesCallback?: (
|
||||||
|
sender: Runtime.MessageSender
|
||||||
|
) => Promise<LanguagePair>;
|
||||||
|
setCurrentLanguagesCallback?: (
|
||||||
|
value: Partial<LanguagePair>,
|
||||||
|
sender: Runtime.MessageSender
|
||||||
|
) => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
browser.runtime.onMessage.addListener(
|
browser.runtime.onMessage.addListener(
|
||||||
(c: command, s: Runtime.MessageSender) => {
|
(c: command, s: Runtime.MessageSender) => {
|
||||||
try {
|
|
||||||
switch (c.commandKind) {
|
switch (c.commandKind) {
|
||||||
case commandKinds.setEnabled:
|
case commandKinds.setEnabled:
|
||||||
return this.setEnabledCallback!(c.value, s);
|
return this.setEnabledCallback!(c.value, s);
|
||||||
|
@ -37,14 +43,13 @@ export class Communicator {
|
||||||
return this.removeFlashcardCallback!(c.card, s);
|
return this.removeFlashcardCallback!(c.card, s);
|
||||||
case commandKinds.getLanguages:
|
case commandKinds.getLanguages:
|
||||||
return this.getLanguagesCallback!(s);
|
return this.getLanguagesCallback!(s);
|
||||||
|
case commandKinds.getCurrentLanguages:
|
||||||
|
return this.getCurrentLanguagesCallback!(s);
|
||||||
|
case commandKinds.setCurrentLanguages:
|
||||||
|
return this.setCurrentLanguagesCallback!(c.value, s);
|
||||||
default:
|
default:
|
||||||
console.warn(`Unimplemented command ${c}`);
|
console.warn(`Unimplemented command ${c}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof ReferenceError)
|
|
||||||
console.warn(`Unimplemented command ${c}`);
|
|
||||||
else throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -72,6 +77,12 @@ export class Communicator {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
commandKind: commandKinds.getLanguages,
|
commandKind: commandKinds.getLanguages,
|
||||||
});
|
});
|
||||||
|
static getCurrentLanguages = (): Promise<LanguagePair> =>
|
||||||
|
sendMessage({
|
||||||
|
commandKind: commandKinds.getCurrentLanguages,
|
||||||
|
});
|
||||||
|
static setCurrentLanguages = (v: Partial<LanguagePair>) =>
|
||||||
|
sendMessage({ commandKind: commandKinds.setCurrentLanguages, value: v });
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendMessage = (m: command) => browser.runtime.sendMessage(m);
|
const sendMessage = (m: command) => browser.runtime.sendMessage(m);
|
||||||
|
@ -83,17 +94,22 @@ export enum commandKinds {
|
||||||
addFlashcard = "addFlashcard",
|
addFlashcard = "addFlashcard",
|
||||||
removeFlashcard = "removeFlashcard",
|
removeFlashcard = "removeFlashcard",
|
||||||
getLanguages = "getLanguages",
|
getLanguages = "getLanguages",
|
||||||
|
getCurrentLanguages = "getCurrentLanguage",
|
||||||
|
setCurrentLanguages = "setCurrentLanguages",
|
||||||
}
|
}
|
||||||
interface setEnabled {
|
interface setEnabled {
|
||||||
commandKind: commandKinds.setEnabled;
|
commandKind: commandKinds.setEnabled;
|
||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface getEnabled {
|
interface getCommand {
|
||||||
commandKind: commandKinds.getEnabled;
|
commandKind:
|
||||||
|
| commandKinds.getEnabled
|
||||||
|
| commandKinds.getLanguages
|
||||||
|
| commandKinds.getCurrentLanguages;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface translate {
|
interface translateCommand {
|
||||||
commandKind: commandKinds.translate;
|
commandKind: commandKinds.translate;
|
||||||
toTranslate: string;
|
toTranslate: string;
|
||||||
}
|
}
|
||||||
|
@ -108,14 +124,15 @@ interface removeFlashcard {
|
||||||
card: Flashcard;
|
card: Flashcard;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface getLanguages {
|
interface setCurrentLanguages {
|
||||||
commandKind: commandKinds.getLanguages;
|
commandKind: commandKinds.setCurrentLanguages;
|
||||||
|
value: Partial<LanguagePair>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type command =
|
export type command =
|
||||||
| setEnabled
|
| setEnabled
|
||||||
| getEnabled
|
| getCommand
|
||||||
| translate
|
| translateCommand
|
||||||
| addFlashcard
|
| addFlashcard
|
||||||
| removeFlashcard
|
| removeFlashcard
|
||||||
| getLanguages;
|
| setCurrentLanguages;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
<div id="spinner" />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#spinner {
|
#spinner {
|
||||||
border: 0.3em solid #f3f3f3;
|
border: 0.3em solid #f3f3f3;
|
||||||
|
@ -18,5 +20,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="spinner" />
|
|
||||||
|
|
|
@ -14,6 +14,25 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="translatedContainer">
|
||||||
|
<span>{trans.result}</span>
|
||||||
|
|
||||||
|
{#if !card}
|
||||||
|
<button on:click={addFlashcard}>
|
||||||
|
Add
|
||||||
|
<br />
|
||||||
|
card
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button on:click={removeFlashcard} class="enabled">
|
||||||
|
Remove
|
||||||
|
<br />
|
||||||
|
card
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "../color_scheme";
|
@use "../color_scheme";
|
||||||
|
|
||||||
|
@ -47,22 +66,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="translatedContainer">
|
|
||||||
<span>{trans.result}</span>
|
|
||||||
|
|
||||||
{#if !card}
|
|
||||||
<button on:click={addFlashcard}>
|
|
||||||
Add
|
|
||||||
<br />
|
|
||||||
card
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button on:click={removeFlashcard} class="enabled">
|
|
||||||
Remove
|
|
||||||
<br />
|
|
||||||
card
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -8,6 +8,17 @@
|
||||||
label.toLowerCase().startsWith(filterText.toLowerCase());
|
label.toLowerCase().startsWith(filterText.toLowerCase());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="select-styling">
|
||||||
|
<Select
|
||||||
|
items={langs}
|
||||||
|
getOptionLabel={getLabel}
|
||||||
|
getSelectionLabel={getLabel}
|
||||||
|
optionIdentifier="code"
|
||||||
|
{itemFilter}
|
||||||
|
isClearable={false}
|
||||||
|
bind:selectedValue />
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "../color_scheme.scss";
|
@use "../color_scheme.scss";
|
||||||
.select-styling {
|
.select-styling {
|
||||||
|
@ -20,14 +31,3 @@
|
||||||
--border: none;
|
--border: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="select-styling">
|
|
||||||
<Select
|
|
||||||
items={langs}
|
|
||||||
getOptionLabel={getLabel}
|
|
||||||
getSelectionLabel={getLabel}
|
|
||||||
optionIdentifier="code"
|
|
||||||
{itemFilter}
|
|
||||||
isClearable={false}
|
|
||||||
bind:selectedValue />
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -4,18 +4,33 @@
|
||||||
|
|
||||||
Communicator.getEnabled().then((v) => (enabled = v));
|
Communicator.getEnabled().then((v) => (enabled = v));
|
||||||
|
|
||||||
|
const languages = Communicator.getLanguages();
|
||||||
|
|
||||||
let enabled: boolean;
|
let enabled: boolean;
|
||||||
$: if (enabled !== undefined) Communicator.setEnabled(enabled);
|
$: if (enabled !== undefined) Communicator.setEnabled(enabled);
|
||||||
|
|
||||||
const langPromise = Communicator.getLanguages();
|
let selected: LanguagePair;
|
||||||
let srcLang: Language;
|
Communicator.getCurrentLanguages().then((l) => (selected = l));
|
||||||
let dstLang: Language;
|
$: if (selected) Communicator.setCurrentLanguages(selected);
|
||||||
langPromise.then((langs) => {
|
|
||||||
srcLang = langs[0];
|
|
||||||
dstLang = langs[0];
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
|
||||||
|
<label class:enabled>
|
||||||
|
<input type="checkbox" bind:checked={enabled} />
|
||||||
|
{enabled ? 'Disable Extension' : 'Enable Extension'}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{#if selected}
|
||||||
|
{#await languages then langs}
|
||||||
|
Translate from
|
||||||
|
<LanguageSelection {langs} bind:selectedValue={selected.srcLang} />
|
||||||
|
To
|
||||||
|
<LanguageSelection {langs} bind:selectedValue={selected.dstLang} />
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "../color_scheme.scss";
|
@use "../color_scheme.scss";
|
||||||
|
|
||||||
|
@ -59,18 +74,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="container">
|
|
||||||
|
|
||||||
<label class:enabled>
|
|
||||||
<input type="checkbox" bind:checked={enabled} />
|
|
||||||
{enabled ? 'ON' : 'OFF'}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{#await langPromise then langs}
|
|
||||||
Translate from
|
|
||||||
<LanguageSelection {langs} bind:selectedValue={srcLang} />
|
|
||||||
To
|
|
||||||
<LanguageSelection {langs} bind:selectedValue={dstLang} />
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
import App from "./Popup.svelte";
|
import App from "./Popup.svelte";
|
||||||
new App({ target: document.body });
|
new App({ target: document.body });
|
||||||
|
document.addEventListener;
|
||||||
|
|
|
@ -13,5 +13,11 @@
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
"background": { "scripts": ["background.bundle.js"] },
|
"background": { "scripts": ["background.bundle.js"] },
|
||||||
"permissions": ["<all_urls>", "sessions", "webRequest", "webRequestBlocking"]
|
"permissions": [
|
||||||
|
"<all_urls>",
|
||||||
|
"sessions",
|
||||||
|
"webRequest",
|
||||||
|
"webRequestBlocking",
|
||||||
|
"storage"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
7
src/types.d.ts
vendored
7
src/types.d.ts
vendored
|
@ -6,6 +6,7 @@ declare module "*?file" {
|
||||||
declare interface Translation {
|
declare interface Translation {
|
||||||
src: string;
|
src: string;
|
||||||
result: string;
|
result: string;
|
||||||
|
languages: LanguagePair;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface Flashcard {
|
declare interface Flashcard {
|
||||||
|
@ -14,6 +15,12 @@ declare interface Flashcard {
|
||||||
result: string;
|
result: string;
|
||||||
dateAdded: Date;
|
dateAdded: Date;
|
||||||
exported: boolean;
|
exported: boolean;
|
||||||
|
languages: LanguagePair;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface LanguagePair {
|
||||||
|
srcLang: Language;
|
||||||
|
dstLang: Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface Language {
|
declare interface Language {
|
||||||
|
|
18
src/utils.ts
Normal file
18
src/utils.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
type resolveCallback<T> = (value: T) => void;
|
||||||
|
type rejectCallback = (reason: any) => void;
|
||||||
|
|
||||||
|
export const newPromise = <T>(): [
|
||||||
|
Promise<T>,
|
||||||
|
resolveCallback<T>,
|
||||||
|
rejectCallback
|
||||||
|
] => {
|
||||||
|
let resolve: resolveCallback<T>;
|
||||||
|
let reject: rejectCallback;
|
||||||
|
const promise = new Promise(
|
||||||
|
(res: resolveCallback<T>, rej: rejectCallback) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return [promise, resolve!, reject!];
|
||||||
|
};
|
Loading…
Reference in a new issue