aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2025-09-23 01:03:19 +0530
committerBobby <[email protected]>2025-09-23 01:03:19 +0530
commit3d605293b5e33e734a7d6c6d749ab2f72b020628 (patch)
tree9fd5cf954c645da0002225949dbba8ffd67718b7
parent5562007f1ba8ff0247ce24f55bddf5717a84f5a3 (diff)
downloadthunderbird-ai-compose-3d605293b5e33e734a7d6c6d749ab2f72b020628.tar.xz
thunderbird-ai-compose-3d605293b5e33e734a7d6c6d749ab2f72b020628.zip
rudimentary popup extension
-rw-r--r--src/background.ts68
-rw-r--r--src/manifest.json20
-rw-r--r--src/options/options.html35
-rw-r--r--src/options/options.ts27
-rw-r--r--src/popup/popup.html37
-rw-r--r--src/popup/popup.ts18
6 files changed, 192 insertions, 13 deletions
diff --git a/src/background.ts b/src/background.ts
new file mode 100644
index 0000000..997dd68
--- /dev/null
+++ b/src/background.ts
@@ -0,0 +1,68 @@
+type Identity = {
+ id?: string;
+ email?: string;
+ name?: string;
+};
+
+type ComposeContext = {
+ account: Identity;
+ compose: {
+ subject?: string;
+ to?: string[];
+ cc?: string[];
+ bcc?: string[];
+ bodyPlain?: string;
+ bodyHTML?: string;
+ identityId?: string;
+ };
+};
+
+async function getComposeContext(): Promise<ComposeContext> {
+ const data: ComposeContext = { account: {}, compose: {} };
+
+ try {
+ const tabs = await browser.tabs.query({ active: true, currentWindow: true });
+ const tabId = tabs[0]?.id;
+ if (!tabId) return data;
+
+ const details: any = await (browser as any).compose.getComposeDetails(tabId);
+
+ data.compose.subject = details?.subject ?? "";
+ data.compose.to = details?.to ?? [];
+ data.compose.cc = details?.cc ?? [];
+ data.compose.bcc = details?.bcc ?? [];
+ data.compose.identityId = details?.identityId ?? undefined;
+ data.compose.bodyPlain = details?.plainTextBody ?? "";
+ data.compose.bodyHTML = details?.body ?? "";
+
+ const accounts: any[] = (browser as any).accounts?.list
+ ? await (browser as any).accounts.list()
+ : [];
+
+ if (data.compose.identityId) {
+ for (const acc of accounts) {
+ for (const ident of acc.identities) {
+ if (ident.id === data.compose.identityId) {
+ data.account = {
+ id: ident.id,
+ email: ident.email,
+ name: ident.name,
+ };
+ }
+ }
+ }
+ }
+ } catch (err) {
+ console.warn("getComposeContext failed:", err);
+ }
+
+ return data;
+}
+
+// Handle requests from popup/options
+browser.runtime.onMessage.addListener(async (message: any) => {
+ if (message?.type === "getComposeContext") {
+ return await getComposeContext();
+ }
+ return { error: "unknown message" };
+});
diff --git a/src/manifest.json b/src/manifest.json
index e61aad4..fb7c621 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 3,
+ "manifest_version": 2,
"name": "Thunderbird AI Compose",
"version": "0.1.0",
"description": "Thunderbird Mail Extension that helps you compose emails using AI",
@@ -7,13 +7,15 @@
"compose",
"messagesRead",
"accountsRead",
- "storage",
- "commands"
+ "storage"
],
"background": {
- "service_worker": "background.js"
+ "scripts": [
+ "browser-polyfill.js",
+ "background.js"
+ ]
},
- "action": {
+ "compose_action": {
"default_title": "Write with AI",
"default_popup": "popup/popup.html"
},
@@ -21,14 +23,6 @@
"page": "options/options.html",
"open_in_tab": false
},
- "commands": {
- "open-popup": {
- "suggested_key": {
- "default": "Alt+I"
- },
- "description": "Open AI composer popup"
- }
- },
"applications": {
"gecko": {
diff --git a/src/options/options.html b/src/options/options.html
new file mode 100644
index 0000000..bf70f4c
--- /dev/null
+++ b/src/options/options.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>AI Compose Settings</title>
+ <style>
+ body {
+ font-family: sans-serif;
+ padding: 10px;
+ }
+ label {
+ display: block;
+ margin: 10px 0 4px;
+ }
+ input {
+ width: 100%;
+ padding: 6px;
+ }
+ button {
+ margin-top: 10px;
+ padding: 6px 12px;
+ }
+ </style>
+ </head>
+ <body>
+ <h2>Settings</h2>
+ <label>Proxy Endpoint <input id="endpoint" type="url" /></label>
+ <label>Auth Token <input id="token" type="text" /></label>
+ <button id="save">Save</button>
+ <p id="msg"></p>
+
+ <script src="../browser-polyfill.js"></script>
+ <script src="options.js"></script>
+ </body>
+</html>
diff --git a/src/options/options.ts b/src/options/options.ts
new file mode 100644
index 0000000..8574ab8
--- /dev/null
+++ b/src/options/options.ts
@@ -0,0 +1,27 @@
+type Settings = {
+ proxyEndpoint?: string;
+ proxyToken?: string;
+};
+
+const endpointInput: HTMLInputElement = document.getElementById("endpoint") as HTMLInputElement;
+const tokenInput: HTMLInputElement = document.getElementById("token") as HTMLInputElement;
+const saveBtn: HTMLButtonElement = document.getElementById("save") as HTMLButtonElement;
+const msg: HTMLElement = document.getElementById("msg") as HTMLElement;
+
+async function loadSettings(): Promise<void> {
+ const settings: Settings = await browser.storage.local.get(["proxyEndpoint", "proxyToken"]);
+ endpointInput.value = settings.proxyEndpoint ?? "";
+ tokenInput.value = settings.proxyToken ?? "";
+}
+
+async function saveSettings(): Promise<void> {
+ await browser.storage.local.set({
+ proxyEndpoint: endpointInput.value.trim(),
+ proxyToken: tokenInput.value.trim(),
+ });
+ msg.textContent = "Saved!";
+ setTimeout(() => (msg.textContent = ""), 2000);
+}
+
+saveBtn.addEventListener("click", saveSettings);
+loadSettings();
diff --git a/src/popup/popup.html b/src/popup/popup.html
new file mode 100644
index 0000000..808bc9e
--- /dev/null
+++ b/src/popup/popup.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>AI Prompt</title>
+ <style>
+ body {
+ font-family: sans-serif;
+ padding: 10px;
+ }
+ textarea {
+ width: 100%;
+ height: 120px;
+ }
+ button {
+ margin-top: 8px;
+ padding: 6px 12px;
+ }
+ pre {
+ background: #f5f5f5;
+ padding: 6px;
+ max-height: 200px;
+ overflow: auto;
+ }
+ </style>
+ </head>
+ <body>
+ <h3>Enter Prompt</h3>
+ <textarea id="prompt"></textarea>
+ <br />
+ <button id="send">Show Compose Context</button>
+ <pre id="output">No data yet</pre>
+
+ <script src="../browser-polyfill.js"></script>
+ <script src="popup.js"></script>
+ </body>
+</html>
diff --git a/src/popup/popup.ts b/src/popup/popup.ts
new file mode 100644
index 0000000..78866e9
--- /dev/null
+++ b/src/popup/popup.ts
@@ -0,0 +1,18 @@
+type Payload = {
+ prompt: string;
+ context: any;
+};
+
+const promptEl: HTMLTextAreaElement = document.getElementById("prompt") as HTMLTextAreaElement;
+const sendBtn: HTMLButtonElement = document.getElementById("send") as HTMLButtonElement;
+const outputEl: HTMLElement = document.getElementById("output") as HTMLElement;
+
+sendBtn.addEventListener("click", async (): Promise<void> => {
+ const ctx: any = await browser.runtime.sendMessage({ type: "getComposeContext" });
+ const payload: Payload = {
+ prompt: promptEl.value,
+ context: ctx,
+ };
+ outputEl.textContent = JSON.stringify(payload, null, 2);
+ alert(JSON.stringify(payload, null, 2));
+});