diff --git a/AGENTS.md b/AGENTS.md index 30bc1ff..ae4db40 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,6 +35,17 @@ Firefox Desktop is the only current implementation and release target. Do not ad Use two-space indentation in JSON, YAML, and JavaScript configuration. Preserve logical key grouping in `manifest.json`. Use lowercase, hyphenated filenames and include dimensions in icon names. Keep changes focused and avoid adding runtime dependencies without a concrete need. +## Options Page + +- Keep every setting. Customization in a lightweight extension is what sets + this one apart, so simplify wording and layout, but never remove a setting + without the maintainer's agreement. Less-used settings can sit under + **More options**. +- Write hotkeys as `+L`: named keys in angle brackets, single characters + plain. `options/shortcut.js` swaps in Mac key names. +- Say that ChatGPT opens with the text filled in, never that the text is sent, + searched, or asked. The options design spec explains why. + ## Testing Guidelines Unit tests cover the pure logic in `lib/` via `node:test`; automated checks @@ -46,10 +57,12 @@ After `npm test`, use `npm run dev` and confirm: - `gpt ` opens ChatGPT, the provider stays non-default, and Firefox search settings can configure it. -- The Options/Preferences tab renders the settings plus Restore defaults, +- The Options/Preferences tab renders the drawings, every setting (including + those under More options), and Restore defaults, in both light and dark themes and at a narrow window width, and values - persist across a restart. -- **Context menu → Simple** shows the direct **Ask ChatGPT** item only for + persist across a restart. Expanding and collapsing More options and the + collapsed sections resizes the inline panel without clipping or scrolling. +- **Right-click menu → Simple** shows the direct **Ask ChatGPT** item only for selected text. **Advanced** shows the contextual submenu; selection takes precedence over links. **Off** hides the menu. Switch modes without restarting and confirm Alt+Shift+G still works in every mode. Restore defaults returns @@ -74,7 +87,7 @@ After `npm test`, use `npm run dev` and confirm: 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 + Check restricted-page fallback, right-click 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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f7eb88..353131a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. See [commit ## Unreleased - Use the extension's bundled icon for the ChatGPT search engine. +- Rewrite the options page for first-time users: usage instructions with a + small drawing for each way to use the extension, a preview of the Advanced + right-click menu, plainer wording, and the prompt template, temporary chat, + and web search settings under More options. Hotkeys are written as + `+L`, with Mac key names on a Mac. +- Correct the keyword instructions: a custom keyword works alongside `gpt` + rather than replacing it. ## [1.2.0] (2026-10-18) diff --git a/README.md b/README.md index 48e80c4..1f65807 100644 --- a/README.md +++ b/README.md @@ -31,23 +31,24 @@ 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. +tab/window preferences, and works even when the right-click 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 +The options panel's collapsed **Change the keyboard shortcut** section 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 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 +To customize the prompt, open **More options** and set **Prompt template** to something like `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 Open Firefox’s **Settings → Search**, or paste `about:preferences#search` into -the address bar. Under **Search Shortcuts**, edit ChatGPT’s keyword to replace -`gpt` with your preferred prefix. Under **Default Search Engine**, choose +the address bar. Under **Search Shortcuts**, edit ChatGPT’s keyword to add +your own prefix; `gpt` keeps working alongside it. Under **Default Search Engine**, choose ChatGPT to search without a prefix, or choose your previous engine to switch back. See [Firefox’s search settings guide](https://support.mozilla.org/en-US/kb/change-your-default-search-settings-firefox) for illustrated instructions. @@ -60,21 +61,25 @@ not change them. 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; +- **Right-click menu** — **Simple** (default) keeps the direct selection action; **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 +- **Show Translate in the Advanced menu** — off by default. When enabled, choose **Ask each time** for a language dialog, or enter a **Preferred language** to translate directly. An empty or invalid preferred language falls back to asking. Enter continues; Escape or Cancel opens nothing. +- **Open ChatGPT in** — a new tab, a new background tab, or a new window. + +Under **More options**, which opens by itself once you have changed one of +them: + +- **Prompt template** — wraps your text, for example `Explain simply: {query}`. + Up to 1,000 characters. - **Request a temporary chat** — asks ChatGPT not to save the conversation. ChatGPT may not honour this; check its own indicator before relying on it. - **Ask ChatGPT to use web search** — a hint to ChatGPT's interface, not a guarantee. -- **Open in** — a new tab, a new background tab, or a new window. -- **Prompt template** — wraps your text, for example `Explain simply: {query}`. - Up to 1,000 characters. Restore defaults resets extension preferences to Simple mode with translation hidden; it does not change Firefox's search engine or shortcut settings. diff --git a/docs/superpowers/specs/2026-10-18-options-page-for-first-time-users-design.md b/docs/superpowers/specs/2026-10-18-options-page-for-first-time-users-design.md new file mode 100644 index 0000000..1694a3b --- /dev/null +++ b/docs/superpowers/specs/2026-10-18-options-page-for-first-time-users-design.md @@ -0,0 +1,125 @@ +# Options page for first-time users + +Status: implemented on `feat/options-page-cleanup` (PR #23). + +## Goal + +Make the options page usable by people who are not comfortable with computers, +without losing any setting. Customization in a lightweight extension is what +sets this one apart, so the page simplifies wording and layout, never features. + +## Layout + +1. **Description**, no heading: "This extension adds ChatGPT as a Firefox + search engine." +2. **Usage instructions**: one labelled line per way to open ChatGPT, each with + one drawing: + - Address bar + - Right-click + - Keyboard (hidden when no shortcut is assigned) + - Toolbar + + A small tip follows: ChatGPT opens with the text filled in, press + `` if it isn't sent, and sign-in may be needed. +3. **Settings**: + - Visible: **Right-click menu** (renamed from "Context menu"), its + Translate sub-settings, and **Open ChatGPT in**. + - Choosing **Advanced** reveals a drawing of the Ask ChatGPT submenu. It + sits in the block that only shows for Advanced, so it previews the one + setting newcomers find hardest to picture, at no cost to anyone else. + - Under **More options**: prompt template, temporary chat, and web search. + The summary names all three. The section opens on load, or when a sync + changes one of them, if any differs from its default. That way a + customised setting never looks lost. + - **Restore defaults** sits on the right in red, because it discards every + customised setting at once. +4. Two collapsed sections: + - **Use your own keyword or make ChatGPT your default search engine**, as + numbered steps. + - **Change the keyboard shortcut**. +5. Project links, centred, stacked on narrow panels. + +## Drawings, not screenshots + +Each drawing is HTML, CSS, and a little inline SVG. The only image is the +bundled extension icon. The drawings are `aria-hidden`; the text next to them +carries the instructions. + +Real screenshots were considered and rejected for the page: +- they go stale when Firefox redesigns its address bar or menus; +- the right-click menu looks different on Windows, macOS, and Linux; +- they need separate light and dark versions; +- they must be bundled, since loading them remotely would repeat the problem + fixed in #20. + +Size was not the issue. Screenshots would have added roughly 50–150 KB. The +whole rewrite, drawings included, added under 4 KB; the package went from +52.8 KB to 56.5 KB. +Real screenshots belong on the AMO listing instead, which has none. + +The address-bar drawing shows a ChatGPT label after `gpt` and a space. The +basis is the code shipped in Firefox 157.0.1, read from its `omni.ja`; it has +not yet been checked on screen: +- `UrlbarProviderAliasEngines` gives keyword results a `keyword` payload; +- `UrlbarInputBase.onFirstResult` enters search mode for such a result. + +If Firefox shows no label, the drawing should show plain text instead. + +The keyboard drawing draws key caps for the user's actual shortcut, which +`options/shortcut.js` builds from `commands.getAll()`. + +## Hotkeys + +Hotkeys are written as `+L`: named keys in angle brackets, single +characters plain. This is the maintainer's own convention from the AMO +listing. It applies to the static text and to the live shortcut. + +On macOS the page uses Mac key names, detected with +`runtime.getPlatformInfo()`, which needs no permission: +- `Ctrl` and `Command` become `Cmd`; Firefox reads a manifest's `Ctrl` as + Command there (`ShortcutUtils.chromeModifierKeyMap`). +- `MacCtrl` becomes `Ctrl`. +- `Alt` becomes `Option`. +- `Enter` becomes `Return`. + +Shortcuts set in Firefox's shortcut editor on macOS are stored with +`MacCtrl`, `Alt`, `Command`, and `Shift` (`addon-shortcuts.mjs`, +`getShortcutForEvent`). + +## Copy rules kept + +These come from the +[options design](2026-07-29-extension-options-design.md): +- Copy says ChatGPT opens with the text filled in, never that it is sent, + searched, or asked. +- The temporary-chat label and warning are unchanged, with no "private", + "incognito", or "secure" near them. +- Web search stays labelled as a hint. + +## Testing + +`npm test` covers: +- the key formatter, including the macOS key names; +- the static hotkeys in `options.html` matching the formatter; +- the key caps drawn for the live shortcut. + +Manual checks are listed in `AGENTS.md`. + +To preview the page outside the extension: +1. Copy `options/`, `lib/`, and `icons/` to a scratch folder. +2. Add a script before `options.js` that defines a stand-in `browser` with + `runtime.getPlatformInfo`, `commands`, and `storage`. +3. Serve the folder on localhost. +4. Run `firefox --headless -no-remote -profile --screenshot out.png + --window-size=680,1600 `. + +For dark mode, give the throwaway profile +`layout.css.prefers-color-scheme.content-override` = 0 and +`ui.systemUsesDarkTheme` = 1. This renders with Firefox's own engine, but not +inside `about:addons`. + +## Not in scope + +- Screenshots and an updated description for the AMO listing. +- Pinning the toolbar button by default (`browser_action.default_area`). +- Rewriting the README's hotkeys in the `++G` style. diff --git a/options/options.css b/options/options.css index aab5767..721f7eb 100644 --- a/options/options.css +++ b/options/options.css @@ -5,6 +5,11 @@ --border: #8f8f9d; --field-bg: #ffffff; --accent: #0060df; + /* Backdrop for the drawings and inline code. */ + --surface: #f0f0f4; + --tooltip-bg: #15141a; + --tooltip-fg: #fbfbfe; + --danger: #d70022; } @media (prefers-color-scheme: dark) { @@ -15,6 +20,10 @@ --border: #8f8f9d; --field-bg: #2b2a33; --accent: #0df; + --surface: #1c1b22; + --tooltip-bg: #52525e; + --tooltip-fg: #fbfbfe; + --danger: #ff848b; } } @@ -30,7 +39,270 @@ body { } .intro { - margin: 0 0 1.5rem; + margin: 0 0 0.75rem; +} + +.ways { + margin: 0 0 0.75rem; + padding: 0; + list-style: none; +} + +.ways > li { + margin-bottom: 0.9rem; +} + +.inline-icon { + width: 16px; + height: 16px; + vertical-align: -3px; +} + +/* Simplified drawings of Firefox's address bar and right-click menu. They are + not screenshots, so they follow the theme and never go out of date. */ +.drawing { + max-width: 24rem; + margin: 0.45rem auto 0; + padding: 0.75rem; + border-radius: 8px; + background: var(--surface); + font-size: 0.85rem; + user-select: none; +} + +.drawing img { + width: 16px; + height: 16px; +} + +.drawing-urlbar { + display: flex; + align-items: center; + gap: 0.45rem; + padding: 0.35rem 0.5rem; + border-radius: 6px; + background: var(--field-bg); + outline: 2px solid var(--accent); +} + +.drawing-chip { + display: inline-flex; + align-items: center; + gap: 0.3rem; + padding: 0.1rem 0.45rem; + border-radius: 4px; + background: color-mix(in srgb, var(--fg) 10%, var(--field-bg)); + font-weight: 600; + white-space: nowrap; +} + +.drawing-typed { + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +/* The text cursor. */ +.drawing-typed::after { + content: ""; + display: inline-block; + width: 1px; + height: 1.1em; + margin-inline-start: 1px; + background: currentColor; + vertical-align: text-bottom; +} + +.drawing-page { + margin: 0 0 0.3rem; +} + +.drawing mark { + color: inherit; + background: color-mix(in srgb, var(--accent) 30%, transparent); +} + +.drawing-toolbar { + position: relative; + display: flex; + align-items: center; + gap: 0.35rem; +} + +.drawing-address { + flex: 1; + min-width: 0; + padding: 0.3rem 0.6rem; + border-radius: 6px; + background: var(--field-bg); + color: var(--fg-dim); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.drawing-button { + position: relative; + display: inline-flex; + flex: none; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 4px; +} + +.drawing-button svg { + width: 16px; + height: 16px; + fill: none; + stroke: currentColor; + stroke-width: 1.3; + stroke-linecap: round; + stroke-linejoin: round; +} + +.drawing-button-hover { + background: color-mix(in srgb, var(--fg) 14%, transparent); +} + +/* The mouse pointer resting on the hovered button. */ +.drawing-button svg.drawing-pointer { + position: absolute; + top: 16px; + left: 17px; + width: 12px; + height: 18px; + fill: #fff; + stroke: #000; + stroke-width: 1; +} + +.drawing-tooltip { + position: absolute; + top: calc(100% + 0.65rem); + right: 0; + padding: 0.2rem 0.5rem; + border-radius: 4px; + background: var(--tooltip-bg); + color: var(--tooltip-fg); + white-space: nowrap; + box-shadow: 0 1px 4px rgb(0 0 0 / 0.3); +} + +/* Top padding keeps the heading clear of the tooltip at narrow widths. */ +.drawing-content { + margin: 0.6rem 0 0; + padding: 1.9rem 0.75rem 1rem; + border-radius: 6px; + background: var(--field-bg); + font-size: 1.05rem; + font-weight: 700; +} + +.drawing-menu { + width: 11rem; + /* Opens at the pointer, just under the selection. */ + margin-inline-start: 3.5rem; + padding: 0.25rem 0; + border: 1px solid color-mix(in srgb, var(--border) 60%, transparent); + border-radius: 6px; + background: var(--field-bg); + box-shadow: 0 2px 8px rgb(0 0 0 / 0.2); +} + +.drawing-menu > div { + display: flex; + align-items: center; + gap: 0.45rem; + /* Line the plain items up with the text of the item that has an icon. */ + padding: 0.2rem 0.75rem 0.2rem calc(0.75rem + 16px + 0.45rem); +} + +.drawing-menu > .drawing-menu-active { + padding-inline-start: 0.75rem; + background: color-mix(in srgb, var(--accent) 20%, var(--field-bg)); +} + +.drawing-menu hr { + margin: 0.25rem 0; + border: 0; + border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent); +} + +/* The Advanced submenu, open beside the highlighted Ask ChatGPT item. */ +.drawing-advanced { + margin-bottom: 1.15rem; +} + +.drawing-menus { + display: flex; + align-items: flex-start; + justify-content: center; +} + +.drawing-menus > .drawing-menu { + flex: none; + width: 10rem; + margin-inline-start: 0; +} + +.drawing-menus > .drawing-submenu { + width: 8.5rem; + /* Lines its first item up with Ask ChatGPT, overlapping the menu edge. */ + margin-top: 3.75rem; + margin-inline-start: -0.3rem; +} + +/* Submenu items have no icon, so the highlighted one keeps the icon gap. */ +.drawing-submenu > .drawing-menu-active { + padding-inline-start: calc(0.75rem + 16px + 0.45rem); +} + +.drawing-submenu-arrow { + margin-inline-start: auto; + font-size: 1.2em; + line-height: 1; +} + +/* The shortcut's keys, held down. shortcut.js fills these in. */ +.drawing-keycaps { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.3rem 0; + color: var(--fg-dim); + font-size: 1rem; +} + +.drawing-key { + min-width: 1.4rem; + padding: 0.35rem 0.65rem; + border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border)); + border-bottom-width: 3px; + border-radius: 6px; + background: color-mix(in srgb, var(--accent) 12%, var(--field-bg)); + color: var(--fg); + text-align: center; + transform: translateY(1px); +} + +.hint.intro-hint { + margin: 0; +} + +.usage, +.settings { + margin-top: 1.5rem; + padding-top: 1rem; + border-top: 1px solid var(--border); +} + +.hint.settings-scope { + margin: 0 0 1rem; } .field { @@ -65,7 +337,8 @@ select { input[type="text"]:focus-visible, select:focus-visible, -button:focus-visible { +button:focus-visible, +summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } @@ -76,18 +349,30 @@ button:focus-visible { font-size: 0.85rem; } -.tag { - font-weight: 400; - font-size: 0.75rem; - color: var(--fg-dim); - border: 1px solid var(--border); - border-radius: 3px; - padding: 0 0.3rem; - vertical-align: 1px; +summary { + cursor: pointer; + font-weight: 600; + color: var(--fg); } +details[open] > summary { + margin-bottom: 0.75rem; +} + +.more-options { + margin-bottom: 1.15rem; +} + +.summary-detail { + font-weight: 400; + color: var(--fg-dim); +} + +/* Reversed so the button stays at the right edge and "Saved" appears beside + it without moving it. */ .actions { display: flex; + flex-direction: row-reverse; align-items: center; gap: 0.75rem; flex-wrap: wrap; @@ -104,35 +389,69 @@ button { cursor: pointer; } +/* Restore defaults discards every customised setting at once. */ +button.danger { + color: var(--danger); + border-color: var(--danger); +} + +button.danger:hover { + color: var(--bg); + background: var(--danger); +} + #status { color: var(--fg-dim); font-size: 0.85rem; } .note { - margin-top: 1.75rem; + margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--fg-dim); font-size: 0.85rem; } -code, -kbd { +.note summary { + font-size: 1rem; +} + +code { + padding: 0 0.25em; + border-radius: 3px; + background: var(--surface); font-family: monospace; font-size: 0.9em; } -.note h2 { +/* Written as +L, so the keys read as words rather than code. */ +kbd { + font: inherit; + font-weight: 600; + white-space: nowrap; +} + +.usage h2, +.settings h2 { margin: 0 0 0.5rem; color: var(--fg); font-size: 1rem; } -.note p { +.note p, +.note ol { margin: 0 0 0.75rem; } +.note ol { + padding-inline-start: 1.25rem; +} + +.note li { + margin-bottom: 0.3rem; +} + .note code { overflow-wrap: anywhere; } @@ -140,8 +459,9 @@ kbd { .project-links { display: flex; flex-wrap: wrap; + justify-content: center; gap: 0.5rem 0.65rem; - margin-top: 1rem; + margin-top: 1.5rem; } .project-link { @@ -167,9 +487,23 @@ a:focus-visible { /* The inline about:addons surface can be very narrow. */ @media (max-width: 26rem) { .actions { - align-items: flex-start; + align-items: flex-end; flex-direction: column; } + + .drawing-menu { + margin-inline-start: 1rem; + } + + /* Stack the links rather than wrap one onto a line that starts with "|". */ + .project-links { + flex-direction: column; + align-items: center; + } + + .link-separator { + display: none; + } } [hidden] { display: none !important; } diff --git a/options/options.html b/options/options.html index 0b852e5..95e0b24 100644 --- a/options/options.html +++ b/options/options.html @@ -7,37 +7,120 @@

- This extension adds ChatGPT as a Firefox search engine. Type - gpt your question in the address bar, select text on a webpage and - choose Ask ChatGPT from the right-click menu. + This extension adds ChatGPT as a Firefox search engine.

-

- 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. -

+
+

Usage instructions

-

- ChatGPT may require you to sign in. If your prompt is not sent automatically, - press Enter or click Send in ChatGPT. + +

    +
  • + Address bar: press <Ctrl>+L, + type gpt and a space, then your question, and press + <Enter>. + +
  • +
  • + Right-click: select text on a webpage, right-click it, and + choose Ask ChatGPT. + +
  • + +
  • + Toolbar: select text and click the + + Ask ChatGPT button. With nothing selected, it opens a + blank chat. Can’t see the button? Look in the puzzle-piece Extensions menu. + +
  • +
+ +

+ ChatGPT opens with your text filled in. If it isn’t sent automatically, press + <Enter> or click Send. ChatGPT may ask you to sign in first. +

+
+ +
+

Settings

+

+ These apply to the right-click menu, toolbar button, and keyboard shortcut. + Typing gpt in the address bar isn’t affected.

- + -

Simple asks about selected text. Advanced adds Explain, Summarize, Debug code, and link/page actions.

-

The keyboard shortcut always runs your default action.

+

+ Simple shows Ask ChatGPT. Advanced adds Explain, Summarize, Debug code, + and more. With Off, the toolbar button and keyboard shortcut still work. +