mirror of
https://git.zavage.net/Zavage-Software/search-with-chatgpt-powered-by-openai-extension.git
synced 2026-08-10 21:50:29 -06:00
The plan pinned strict_min_version to 112.0 for the ES-module background floor, but the existing data_collection_permissions key requires Firefox 140 desktop / 142 Android, so addons-linter warned and warningsAsErrors failed the build. Measured: 112.0 -> 2 warnings, 140.0 -> 1, 142.0 -> clean, omitted -> clean. 142.0 was rejected because it locks out ESR 140. Omitting excludes nobody and degrades gracefully below 112. Also syncs the plan's background.js block with the rejection guards added in 22fc754, and reworks two of those comments for clarity.
110 lines
3.2 KiB
JavaScript
110 lines
3.2 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 {
|
|
// Nothing useful to say and nowhere useful to say it: the user expects a
|
|
// tab or nothing at all. Both call sites invoke this without awaiting, so
|
|
// without this catch a failure would log an unhandled rejection.
|
|
}
|
|
}
|
|
|
|
// 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(() => {
|
|
// menus.removeAll() is promise-based and can reject; a failed re-registration
|
|
// cannot be retried usefully from here. (menus.create() is callback-based and
|
|
// cannot reject, so it needs no guard.)
|
|
});
|