mirror of
https://git.zavage.net/Zavage-Software/search-with-chatgpt-powered-by-openai-extension.git
synced 2026-08-10 13:40:29 -06:00
108 lines
3.0 KiB
JavaScript
108 lines
3.0 KiB
JavaScript
import { load } from "../lib/settings.js";
|
|
import { buildChatGptUrl } from "../lib/build-url.js";
|
|
|
|
const MENU_ID = "ask-chatgpt-selection";
|
|
const COMMAND_ID = "ask-chatgpt";
|
|
|
|
// Reads the user's selection for the keyboard-shortcut path. The context menu
|
|
// does not need this — it receives info.selectionText directly.
|
|
//
|
|
// A commands invocation confers activeTab (ext-commands.js grants it before
|
|
// firing onCommand), so executeScript works with no host permission.
|
|
//
|
|
// Failure has three measured shapes, all handled here: a thrown
|
|
// "Missing host permission for the tab", a thrown variant naming frames, and a
|
|
// SILENT resolution to [null] on parent-process about: pages. Never index into
|
|
// a result without a null check.
|
|
async function readSelection() {
|
|
let tabs;
|
|
try {
|
|
tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
|
} catch {
|
|
return "";
|
|
}
|
|
|
|
// Only tab.id may be read. Reading tab.url or tab.title would require the
|
|
// "tabs" permission, which does add an install-prompt warning line.
|
|
const tabId = tabs?.[0]?.id;
|
|
if (typeof tabId !== "number") {
|
|
return "";
|
|
}
|
|
|
|
let results;
|
|
try {
|
|
results = await browser.tabs.executeScript(tabId, {
|
|
code: "window.getSelection().toString()",
|
|
allFrames: true,
|
|
matchAboutBlank: true,
|
|
});
|
|
} catch {
|
|
return "";
|
|
}
|
|
|
|
if (!Array.isArray(results)) {
|
|
return "";
|
|
}
|
|
for (const result of results) {
|
|
if (typeof result === "string" && result.trim() !== "") {
|
|
return result;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
async function openChatGpt(query) {
|
|
try {
|
|
const settings = await load();
|
|
const url = buildChatGptUrl(settings, query);
|
|
|
|
if (settings.openIn === "new-window") {
|
|
try {
|
|
await browser.windows.create({ url });
|
|
return;
|
|
} catch {
|
|
// Fall through to a tab rather than doing nothing.
|
|
}
|
|
}
|
|
|
|
await browser.tabs.create({ url, active: settings.openIn !== "background-tab" });
|
|
} catch {
|
|
// Silence rejections silently — the user expects either a tab to open or
|
|
// nothing, not an error log. Every failure path must degrade gracefully.
|
|
}
|
|
}
|
|
|
|
// removeAll() first, so a background event page waking up and re-running this
|
|
// module cannot throw on a duplicate menu id.
|
|
async function registerMenu() {
|
|
await browser.menus.removeAll();
|
|
browser.menus.create({
|
|
id: MENU_ID,
|
|
title: 'Ask ChatGPT about "%s"',
|
|
contexts: ["selection"],
|
|
});
|
|
}
|
|
|
|
browser.menus.onClicked.addListener((info) => {
|
|
if (info.menuItemId !== MENU_ID) {
|
|
return;
|
|
}
|
|
openChatGpt(info.selectionText ?? "");
|
|
});
|
|
|
|
// browser.commands does not exist on Firefox for Android. This extension is
|
|
// desktop-only, but guard rather than throwing at startup.
|
|
if (browser.commands) {
|
|
browser.commands.onCommand.addListener(async (command) => {
|
|
if (command !== COMMAND_ID) {
|
|
return;
|
|
}
|
|
openChatGpt(await readSelection());
|
|
});
|
|
}
|
|
|
|
registerMenu().catch(() => {
|
|
// Silence rejections from menus.removeAll() — it cannot prevent menu creation
|
|
// on retry, so degrade gracefully with no unhandled rejection logged.
|
|
});
|