From 78e94640e1b7b5e0a1cb2dc514df53f9a57ef88a Mon Sep 17 00:00:00 2001
From: golem
Date: Mon, 12 Oct 2026 12:40:44 -0600
Subject: [PATCH] feat: add Ask ChatGPT toolbar action
---
AGENTS.md | 5 +++
CHANGELOG.md | 2 ++
README.md | 22 +++++++++----
background/background.js | 28 ++++++++++------
docs/toolbar-verification.md | 60 +++++++++++++++++++++++++++++++++
manifest.json | 7 ++++
options/options.html | 10 ++++--
test/toolbar.test.js | 64 ++++++++++++++++++++++++++++++++++++
8 files changed, 180 insertions(+), 18 deletions(-)
create mode 100644 docs/toolbar-verification.md
create mode 100644 test/toolbar.test.js
diff --git a/AGENTS.md b/AGENTS.md
index 7309bc0..30bc1ff 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -72,6 +72,11 @@ After `npm test`, use `npm run dev` and confirm:
into the page → a blank chat is expected. Firefox leaves focus in the
findbar, so no content frame reports focus. This is a deliberate trade: a
stale cross-origin frame silently seeding the prompt was the worse failure.
+- Click the **Ask ChatGPT** toolbar button with no selection, a top-frame
+ selection, and an iframe selection after a stale top-frame selection.
+ Check restricted-page fallback, context menu Off, saved prompt and opening
+ preferences, and consent before shortening long selections. Test both the
+ pinned toolbar button and its entry in Firefox’s extensions menu.
Inspect ZIP contents whenever packaging rules or assets change; `test/` and
`docs/` must never appear, not even as empty directory entries.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 415c9c3..7b94800 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. See [commit
## Unreleased
+- Add an Ask ChatGPT toolbar button: use selected text or open a blank chat,
+ honoring saved prompt and tab/window preferences. No new permissions.
- Add Off / Simple / Advanced context-menu modes with migration from the old
visibility preference. Advanced provides selection presets and link/page URL
actions, with optional translation and a preferred language or language dialog.
diff --git a/README.md b/README.md
index 7200e8d..a82672b 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,12 @@ You can also press **Alt+Shift+G** to use the current selection. With nothing
selected, the shortcut opens a blank chat. Change the shortcut under
**Manage Extension Shortcuts** in the `about:addons` gear menu.
+Click the extension’s **Ask ChatGPT** toolbar button to use selected text or,
+with nothing selected, open a blank chat. On pages Firefox does not allow
+extensions to read, the button opens a blank chat. You can pin the button to
+the toolbar from Firefox’s extensions menu. It uses your saved prompt and
+tab/window preferences, and works even when the context menu is Off.
+
The options panel shows your currently configured shortcut and refreshes when
it changes or you return to the panel. If you remove it, the panel shows
“No keyboard shortcut assigned”. Its **Change keyboard shortcut** button opens
@@ -32,7 +38,7 @@ Firefox’s shortcut editor when supported; otherwise follow the menu path above
**Restore defaults** does not change your keyboard shortcut.
To customize the prompt, set **Prompt template** to something like
-`Explain simply: {query}` or `Translate into Spanish: {query}`. The Simple menu action, Ask normally, and the shortcut use your template.
+`Explain simply: {query}` or `Translate into Spanish: {query}`. The Simple menu action, Ask normally, toolbar button, and shortcut use your template.
Other advanced actions use their own dedicated prompts.
## Search keyword and default engine
@@ -53,8 +59,8 @@ Open `about:addons`, select this extension, and choose the **Options** tab
(**Preferences** on macOS and Linux). Available settings:
- **Context menu** — **Simple** (default) keeps the direct selection action;
- **Advanced** offers contextual presets; **Off** hides the menu. The keyboard
- shortcut works in every mode. Existing enabled/disabled preferences migrate
+ **Advanced** offers contextual presets; **Off** hides the menu. The toolbar
+ button and keyboard shortcut work in every mode. Existing enabled/disabled preferences migrate
to Simple/Off. Changes apply without restarting Firefox.
- **Show Translate in advanced menu** — off by default. When enabled, choose
**Ask each time** for a language dialog, or enter a **Preferred language**
@@ -84,14 +90,14 @@ that cannot fit are rejected. The extension does not simulate submission; ChatGP
prompt is sent automatically or waits in the composer.
These settings apply to selecting text and choosing
-**Ask ChatGPT** from the right-click menu, or pressing
+**Ask ChatGPT** from the right-click menu, clicking the toolbar button, or pressing
Alt+Shift+G. Rebind the shortcut under
**Manage Extension Shortcuts** in the Add-ons gear menu.
The `gpt ` address-bar search is unchanged and does not read these
settings.
-The options panel, the right-click item, and the keyboard shortcut require
+The options panel, toolbar button, right-click item, and keyboard shortcut require
Firefox 112 or newer. On older versions the extension still installs and the
`gpt ` address-bar search still works; only these newer features are
inactive.
@@ -102,6 +108,10 @@ The extension opens ChatGPT with the prompt in the URL. ChatGPT controls what
happens next: it may send the prompt automatically or leave it in the composer.
**If the prompt is not sent automatically, press Enter in ChatGPT.**
+If ChatGPT shows a verification error, try signing in and opening the prompt
+again. Signing in has helped in testing, but does not guarantee automatic
+submission.
+
The extension does not click Send, simulate Enter, or call ChatGPT's internal
conversation API. This behavior can vary with changes to ChatGPT itself.
@@ -145,7 +155,7 @@ reported to Zavage Software.
- Your settings are stored in your own browser profile. If you use Firefox Sync
with **Add-ons** enabled, they sync end-to-end encrypted through your own
Mozilla account.
-- Selection actions and the shortcut place your selected text in the ChatGPT
+- Selection actions, the toolbar button, and the shortcut place your selected text in the ChatGPT
URL. Advanced link/page actions instead place the chosen link or current
page URL in the prompt. URLs may include private query parameters or fragments;
use those actions only for URLs you want to share with ChatGPT.
diff --git a/background/background.js b/background/background.js
index 414cf26..19bd834 100644
--- a/background/background.js
+++ b/background/background.js
@@ -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;
diff --git a/docs/toolbar-verification.md b/docs/toolbar-verification.md
new file mode 100644
index 0000000..eed6643
--- /dev/null
+++ b/docs/toolbar-verification.md
@@ -0,0 +1,60 @@
+# Toolbar action verification — 2026-10-12
+
+The Ask ChatGPT toolbar button uses the existing Ask action: selected text is
+wrapped in the saved prompt; an empty or unreadable selection opens a blank
+chat. Saved opening preferences and shortening consent apply. No new
+permissions, popup, or settings were added.
+
+## Automated checks
+
+- Fresh `npm test` before the PR: strict extension lint reports zero errors,
+ warnings, or notices; all 61 tests across 11 test files pass; ZIP build passes.
+- New toolbar regression covers the clicked tab rather than a later active tab,
+ focused iframe selection with stale parent/sibling selections, empty/unfocused
+ selection, saved prompt/background-tab preferences, and restricted-page
+ fallback with the new-window preference.
+- ZIP inspection excludes docs, tests, and local tool metadata.
+- Independent read-only review found no concrete code defects; the requested
+ browser checks for focus handling and shortening consent were then completed.
+
+## Firefox 157.0.1 checks
+
+Launched with `npm run dev` in temporary headless profiles. Marionette clicked
+Firefox's actual action button; it did not call the extension handler directly.
+
+- Pinned toolbar: top-frame selection reaches the ChatGPT URL; no selection
+ opens `https://chatgpt.com/`.
+- Same-origin and cross-origin iframe selections win over a stale top-frame
+ selection.
+- `about:config` opens a blank chat.
+- Unpinned action in the extensions menu preserves top-frame and cross-origin
+ iframe selections, including a stale parent selection.
+- Changing the prompt through the options UI works with context-menu mode Off.
+- A long selection opens the shortening-consent dialog. Cancel closes it
+ without opening ChatGPT.
+- The pinned icon was visually confirmed in Firefox.
+
+The selection reader retains the shortcut's focused-leaf-frame filter. Actual
+mouse activation through Firefox's toolbar and extensions menu preserved the
+necessary focus in these checks; no broader frame scan was introduced.
+
+The maintainer subsequently confirmed the toolbar button works during desktop
+use. Every editable field type and the full release regression matrix in
+AGENTS.md were not rechecked; existing shortcut-specific manual release checks
+remain outstanding.
+
+## ChatGPT submission limitation
+
+These checks establish that the extension opens the expected URL and preserves
+the selection. They do not guarantee that ChatGPT accepts or automatically
+submits the prompt.
+
+During follow-up testing, URL-supplied prompts showed a ChatGPT verification
+error while signed out on affected machines. Signing in allowed the prompt to
+fill the composer, but it could still require Enter. The verification failure
+also occurred when the URL was entered directly with this extension disabled.
+The cause remains unconfirmed.
+
+The pre-toolbar version and this version generate the same
+`https://chatgpt.com/?q=hello` URL for a default Ask action; the address-bar search
+definition is also unchanged. No forced submission or workaround was added.
diff --git a/manifest.json b/manifest.json
index 167a9b6..715e2f8 100644
--- a/manifest.json
+++ b/manifest.json
@@ -39,6 +39,13 @@
"open_in_tab": false,
"browser_style": false
},
+ "browser_action": {
+ "default_title": "Ask ChatGPT",
+ "default_icon": {
+ "16": "icons/powered-by-openai-icon-16x16.png",
+ "32": "icons/powered-by-openai-icon-32x32.png"
+ }
+ },
"commands": {
"ask-chatgpt": {
"suggested_key": {
diff --git a/options/options.html b/options/options.html
index 2d62280..b1299f2 100644
--- a/options/options.html
+++ b/options/options.html
@@ -13,6 +13,12 @@
ChatGPT.
+
+ Click the Ask ChatGPT toolbar button to ask about selected
+ text, or open a blank chat when nothing is selected. The button works in
+ every context-menu mode and follows your settings below.
+
+