"use strict"; var API_BASE = "https://dash.quadjourney.com"; console.log("[Brain] Background script loaded"); // ── Auth helpers ── function getSession() { return browser.storage.local.get("brainSession").then(function(data) { return data.brainSession || null; }); } function apiRequest(path, opts) { return getSession().then(function(session) { if (!session) throw new Error("Not logged in"); opts = opts || {}; opts.headers = opts.headers || {}; opts.headers["Cookie"] = "platform_session=" + session; opts.credentials = "include"; return fetch(API_BASE + path, opts).then(function(resp) { if (resp.status === 401 || resp.status === 403) { browser.storage.local.remove("brainSession"); throw new Error("Session expired"); } if (!resp.ok) throw new Error("API error: " + resp.status); return resp.json(); }); }); } // ── Save functions ── function saveLink(url, title, note) { var body = { type: "link", url: url }; if (title) body.title = title; if (note && note.trim()) body.raw_content = note.trim(); return apiRequest("/api/brain/items", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); } function saveNote(text) { return apiRequest("/api/brain/items", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ type: "note", raw_content: text }), }); } function saveImage(imageUrl) { return fetch(imageUrl).then(function(resp) { if (!resp.ok) throw new Error("Failed to download image"); return resp.blob(); }).then(function(blob) { var parts = imageUrl.split("/"); var filename = (parts[parts.length - 1] || "image.jpg").split("?")[0]; var formData = new FormData(); formData.append("file", blob, filename); return getSession().then(function(session) { if (!session) throw new Error("Not logged in"); return fetch(API_BASE + "/api/brain/items/upload", { method: "POST", headers: { "Cookie": "platform_session=" + session }, credentials: "include", body: formData, }); }); }).then(function(resp) { if (!resp.ok) throw new Error("Upload failed: " + resp.status); return resp.json(); }); } // ── Badge helpers ── function showBadge(tabId, text, color, duration) { browser.action.setBadgeText({ text: text, tabId: tabId }); browser.action.setBadgeBackgroundColor({ color: color, tabId: tabId }); setTimeout(function() { browser.action.setBadgeText({ text: "", tabId: tabId }); }, duration || 2000); } // ── Context menus ── browser.runtime.onInstalled.addListener(function() { console.log("[Brain] Creating context menus"); browser.contextMenus.create({ id: "save-page", title: "Save page to Brain", contexts: ["page"], }); browser.contextMenus.create({ id: "save-link", title: "Save link to Brain", contexts: ["link"], }); browser.contextMenus.create({ id: "save-image", title: "Save image to Brain", contexts: ["image"], }); }); browser.contextMenus.onClicked.addListener(function(info, tab) { getSession().then(function(session) { if (!session) { browser.action.openPopup(); return; } var promise; if (info.menuItemId === "save-page") { promise = saveLink(tab.url, tab.title); } else if (info.menuItemId === "save-link") { promise = saveLink(info.linkUrl, info.linkText); } else if (info.menuItemId === "save-image") { promise = saveImage(info.srcUrl); } if (promise) { promise.then(function() { showBadge(tab.id, "OK", "#059669", 2000); }).catch(function(e) { showBadge(tab.id, "ERR", "#DC2626", 3000); console.error("[Brain] Save failed:", e); }); } }); }); // ── Keyboard shortcut ── browser.commands.onCommand.addListener(function(command) { if (command === "save-page") { browser.tabs.query({ active: true, currentWindow: true }).then(function(tabs) { var tab = tabs[0]; if (!tab || !tab.url) return; getSession().then(function(session) { if (!session) { browser.action.openPopup(); return; } saveLink(tab.url, tab.title).then(function() { showBadge(tab.id, "OK", "#059669", 2000); }).catch(function(e) { showBadge(tab.id, "ERR", "#DC2626", 3000); }); }); }); } }); // ── Message handler (from popup) ── browser.runtime.onMessage.addListener(function(msg, sender, sendResponse) { console.log("[Brain] Got message:", msg.action); if (msg.action === "login") { fetch(API_BASE + "/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: msg.username, password: msg.password, }), credentials: "include", }).then(function(resp) { if (!resp.ok) { sendResponse({ success: false, error: "Invalid credentials" }); return; } // Wait for cookie to be set return new Promise(function(r) { setTimeout(r, 300); }).then(function() { return browser.cookies.get({ url: API_BASE, name: "platform_session", }); }).then(function(cookie) { if (cookie && cookie.value) { return browser.storage.local.set({ brainSession: cookie.value }).then(function() { sendResponse({ success: true }); }); } sendResponse({ success: false, error: "Login OK but could not capture session" }); }); }).catch(function(e) { console.error("[Brain] Login error:", e); sendResponse({ success: false, error: e.message || "Connection failed" }); }); return true; } if (msg.action === "logout") { browser.storage.local.remove("brainSession").then(function() { sendResponse({ success: true }); }); return true; } if (msg.action === "check-auth") { getSession().then(function(session) { if (!session) { sendResponse({ authenticated: false }); return; } return apiRequest("/api/auth/me").then(function(data) { sendResponse({ authenticated: true, user: data.user || data }); }).catch(function() { sendResponse({ authenticated: false }); }); }); return true; } if (msg.action === "save-link") { saveLink(msg.url, msg.title, msg.note).then(function(result) { sendResponse(result); }).catch(function(e) { sendResponse({ error: e.message }); }); return true; } if (msg.action === "save-note") { saveNote(msg.text).then(function(result) { sendResponse(result); }).catch(function(e) { sendResponse({ error: e.message }); }); return true; } if (msg.action === "save-image") { saveImage(msg.url).then(function(result) { sendResponse(result); }).catch(function(e) { sendResponse({ error: e.message }); }); return true; } });