feat: add Ask ChatGPT toolbar action

This commit is contained in:
golem
2026-10-12 12:51:24 -06:00
parent 5bc670b937
commit 78e94640e1
8 changed files with 180 additions and 18 deletions
+18 -10
View File
@@ -6,11 +6,12 @@ import { initializeMenus } from "./menus.js";
const COMMAND_ID = "ask-chatgpt";
// Reads the user's selection for the keyboard-shortcut path. The context menu
// Reads the user's selection for the shortcut and toolbar. 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.
// Toolbar clicks and command invocations confer activeTab, so executeScript
// works with no host permission. Use the toolbar event's tab ID rather than
// querying again after the user might have switched tabs.
//
// Every frame keeps its own independent Selection, and a selection made in a
// non-focused frame is not cleared when the user selects elsewhere. Without a
@@ -27,17 +28,18 @@ const COMMAND_ID = "ask-chatgpt";
// "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 "";
async function readSelection(tabId) {
if (tabId === undefined) {
try {
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
tabId = tabs?.[0]?.id;
} 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 "";
}
@@ -82,6 +84,12 @@ async function runAction(request) {
}
void initializeMenus({ browser, load, onAction: runAction }).catch(console.error);
if (browser.browserAction) {
browser.browserAction.onClicked.addListener(async tab => {
const source = await readSelection(tab.id);
await runAction({ actionId: "ask", source, settings: await load() });
});
}
if (browser.commands) {
browser.commands.onCommand.addListener(async command => {
if (command !== COMMAND_ID) return;