Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 9be1e88

Browse files
committed
Bug 1970686 - update page chatbot submenu for page summarization r=yjamora,fluent-reviewers,firefox-ai-ml-reviewers,bolsson
Provide content type with context to differentiate page vs selection. Use context for prompt targeting and refactor chatbot footer no longer special page prefix. Update page menu to switch to page type with no selection and default prefs to show summarize with new badge. Add strings for menu updates. Adjust provider url max lengths. Differential Revision: https://phabricator.services.mozilla.com/D254176
1 parent 8097a80 commit 9be1e88

6 files changed

Lines changed: 169 additions & 38 deletions

File tree

browser/app/profile/firefox.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,10 +2114,9 @@ pref("browser.ml.chat.page", false);
21142114
pref("browser.ml.chat.page.footerBadge", true);
21152115
pref("browser.ml.chat.prompt.prefix", '{"l10nId":"genai-prompt-prefix-selection"}');
21162116
pref("browser.ml.chat.prompts.0", '{"id":"summarize","l10nId":"genai-prompts-summarize"}');
2117-
pref("browser.ml.chat.prompts.1", '{"id":"explain","l10nId":"genai-prompts-explain"}');
2118-
pref("browser.ml.chat.prompts.2", '{"id":"simplify","l10nId":"genai-prompts-simplify","targeting":"channel==\'nightly\'"}');
2119-
pref("browser.ml.chat.prompts.3", '{"id":"quiz","l10nId":"genai-prompts-quiz","targeting":"!provider|regExpMatch(\'gemini\') || region == \'US\'"}');
2120-
pref("browser.ml.chat.prompts.4", '{"id":"proofread", "l10nId":"genai-prompts-proofread"}');
2117+
pref("browser.ml.chat.prompts.1", '{"id":"explain","l10nId":"genai-prompts-explain","targeting":"contentType != \'page\'"}');
2118+
pref("browser.ml.chat.prompts.3", '{"id":"quiz","l10nId":"genai-prompts-quiz","targeting":"(!provider|regExpMatch(\'gemini\') || region == \'US\') && contentType != \'page\'"}');
2119+
pref("browser.ml.chat.prompts.4", '{"id":"proofread", "l10nId":"genai-prompts-proofread","targeting":"contentType != \'page\'"}');
21212120
pref("browser.ml.chat.provider", "");
21222121
pref("browser.ml.chat.shortcuts", true);
21232122
pref("browser.ml.chat.shortcuts.custom", true);

browser/components/genai/GenAI.sys.mjs

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ XPCOMUtils.defineLazyPreferenceGetter(
4545
"browser.ml.chat.openSidebarOnProviderChange",
4646
true
4747
);
48+
XPCOMUtils.defineLazyPreferenceGetter(lazy, "chatPage", "browser.ml.chat.page");
4849
XPCOMUtils.defineLazyPreferenceGetter(
4950
lazy,
5051
"chatPromptPrefix",
@@ -122,7 +123,7 @@ export const GenAI = {
122123
link3:
123124
"https://www.anthropic.com/legal/archive/628feec9-7df9-4d38-bc69-fbf104df47b0",
124125
linksId: "genai-settings-chat-claude-links",
125-
maxLength: 15020,
126+
maxLength: 14150,
126127
name: "Anthropic Claude",
127128
tooltipId: "genai-onboarding-claude-tooltip",
128129
},
@@ -142,7 +143,7 @@ export const GenAI = {
142143
link1: "https://openai.com/terms",
143144
link2: "https://openai.com/privacy",
144145
linksId: "genai-settings-chat-chatgpt-links",
145-
maxLength: 14140,
146+
maxLength: 13700,
146147
name: "ChatGPT",
147148
tooltipId: "genai-onboarding-chatgpt-tooltip",
148149
},
@@ -225,7 +226,7 @@ export const GenAI = {
225226
link1: "https://mistral.ai/terms/#terms-of-service-le-chat",
226227
link2: "https://mistral.ai/terms/#privacy-policy",
227228
linksId: "genai-settings-chat-lechat-links",
228-
maxLength: 3680,
229+
maxLength: 13350,
229230
name: "Le Chat Mistral",
230231
tooltipId: "genai-onboarding-lechat-tooltip",
231232
},
@@ -647,32 +648,77 @@ export const GenAI = {
647648
{ provider }
648649
);
649650
menu.menupopup?.remove();
651+
652+
// Determine if we have selection or should use page content
653+
const context = {
654+
contentType: "selection",
655+
selection: nsContextMenu.selectionInfo.fullText ?? "",
656+
};
657+
if (lazy.chatPage && !context.selection) {
658+
// Get page content for prompts when no selection
659+
try {
660+
const actor =
661+
nsContextMenu.browser.browsingContext.currentWindowContext.getActor(
662+
"GenAI"
663+
);
664+
context.selection = await actor.sendQuery("GetReadableText");
665+
context.contentType = "page";
666+
} catch (ex) {
667+
console.warn("Failed to get page content", ex);
668+
}
669+
}
670+
650671
await this.addAskChatItems(
651672
nsContextMenu.browser,
652-
{ selection: nsContextMenu.selectionInfo.fullText ?? "" },
653-
promptObj => menu.appendItem(promptObj.label),
673+
context,
674+
promptObj => {
675+
const item = menu.appendItem(promptObj.label);
676+
if (promptObj.badge) {
677+
item.setAttribute("badge", promptObj.badge);
678+
}
679+
return item;
680+
},
654681
"menu"
655682
);
656683

657-
// Add separator and remove provider option
658-
const hasPrompts = menu.itemCount > 0;
659-
if (hasPrompts) {
660-
menu.menupopup.appendChild(doc.createXULElement("menuseparator"));
661-
const removeItem = menu.appendItem("");
662-
doc.l10n.setAttributes(
663-
removeItem,
664-
provider ? "genai-menu-remove-provider" : "genai-menu-remove-generic",
665-
{ provider }
666-
);
667-
removeItem.addEventListener("command", () => {
668-
Glean.genaiChatbot.contextmenuRemove.record({
669-
provider: this.getProviderId(),
684+
// For page which currently only shows 1 prompt, make it less empty with an
685+
// Open or Choose options depending on provider
686+
if (context.contentType == "page") {
687+
const openItem = menu.appendItem("");
688+
if (lazy.chatProvider && provider) {
689+
doc.l10n.setAttributes(openItem, "genai-menu-open-provider", {
690+
provider,
670691
});
671-
Services.prefs.clearUserPref("browser.ml.chat.provider");
692+
} else {
693+
doc.l10n.setAttributes(
694+
openItem,
695+
lazy.chatProvider
696+
? "genai-menu-open-generic"
697+
: "genai-menu-choose-chatbot"
698+
);
699+
}
700+
openItem.addEventListener("command", () => {
701+
const window = nsContextMenu.browser.ownerGlobal;
702+
window.SidebarController.show("viewGenaiChatSidebar");
672703
});
673704
}
674705

675-
nsContextMenu.showItem(menu, hasPrompts);
706+
// Add remove provider option
707+
menu.menupopup.appendChild(doc.createXULElement("menuseparator"));
708+
const removeItem = menu.appendItem("");
709+
doc.l10n.setAttributes(
710+
removeItem,
711+
provider ? "genai-menu-remove-provider" : "genai-menu-remove-generic",
712+
{ provider }
713+
);
714+
removeItem.addEventListener("command", () => {
715+
Glean.genaiChatbot.contextmenuRemove.record({
716+
provider: this.getProviderId(),
717+
});
718+
Services.prefs.clearUserPref("browser.ml.chat.provider");
719+
});
720+
721+
nsContextMenu.showItem(menu, true);
676722
},
677723

678724
/**
@@ -716,6 +762,20 @@ export const GenAI = {
716762
msg?.attributes.forEach(attr => (toFormat[idx][attr.name] = attr.value))
717763
);
718764

765+
// Specially handle page summarization prompt
766+
if (context.contentType == "page") {
767+
for (const promptObj of toFormat) {
768+
if (promptObj.id == "summarize") {
769+
const [badge, label] = await lazy.l10n.formatValues([
770+
"genai-menu-new-badge",
771+
"genai-menu-summarize-page",
772+
]);
773+
promptObj.badge = badge;
774+
promptObj.label = label;
775+
}
776+
}
777+
}
778+
719779
return lazy.ASRouterTargeting.findMatchingMessage({
720780
messages,
721781
returnAll: true,

browser/components/genai/chat.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@
5050
<div id="browser-container"></div>
5151
<div id="summarize-btn-container">
5252
<span class="badge">New</span>
53-
<moz-button id="summarize-button" size="default"
54-
>Summarize current page
53+
<moz-button
54+
data-l10n-id="genai-page-button-summarize"
55+
id="summarize-button"
56+
size="default"
57+
>
5558
</moz-button>
5659
</div>
5760
</body>

browser/components/genai/chat.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -591,26 +591,18 @@ function showOnboarding(length) {
591591
}
592592

593593
async function summarizeCurrentPage() {
594-
Services.prefs.setCharPref(
595-
"browser.ml.chat.prompt.prefix",
596-
'I\'m on page "%tabTitle%" from "%url%". Use this "%selection|8000%" as my selection.'
597-
);
598-
599594
let browser = topChromeWindow.gBrowser.selectedBrowser;
600595
let actor = browser.browsingContext.currentWindowContext.getActor("GenAI");
601596
let articleTextContent = await actor.sendQuery("GetReadableText");
602597

603598
await lazy.GenAI.addAskChatItems(
604599
browser,
605-
{ selection: articleTextContent },
600+
{ contentType: "page", selection: articleTextContent },
606601
(promptObj, context) => {
607602
if (promptObj.id === "summarize") {
608603
lazy.GenAI.handleAskChat(promptObj, context);
609604
}
610605
},
611-
"page"
606+
"footer"
612607
);
613-
614-
// reset prefix to default
615-
Services.prefs.clearUserPref("browser.ml.chat.prompt.prefix");
616608
}

browser/components/genai/tests/browser/browser_chat_page.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,65 @@ const { sinon } = ChromeUtils.importESModule(
88
"resource://testing-common/Sinon.sys.mjs"
99
);
1010

11+
// Bug 1895789 to standarize contextmenu helpers in BrowserTestUtils
12+
async function openContextMenu() {
13+
const contextMenu = document.getElementById("contentAreaContextMenu");
14+
const promise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
15+
await BrowserTestUtils.synthesizeMouse(
16+
null,
17+
0,
18+
0,
19+
{ type: "contextmenu" },
20+
gBrowser.selectedBrowser
21+
);
22+
await promise;
23+
}
24+
25+
async function hideContextMenu() {
26+
const contextMenu = document.getElementById("contentAreaContextMenu");
27+
const promise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
28+
contextMenu.hidePopup();
29+
await promise;
30+
}
31+
32+
add_setup(async function () {
33+
await SpecialPowers.pushPrefEnv({
34+
set: [["test.wait300msAfterTabSwitch", true]],
35+
});
36+
});
37+
38+
/**
39+
* Check page menu has summarize prompt
40+
*/
41+
add_task(async function test_page_menu_prompt() {
42+
const sandbox = sinon.createSandbox();
43+
const stub = sandbox.stub(GenAI, "handleAskChat");
44+
await SpecialPowers.pushPrefEnv({
45+
set: [
46+
["browser.ml.chat.provider", "http://localhost:8080"],
47+
["browser.ml.chat.page", true],
48+
],
49+
});
50+
await BrowserTestUtils.withNewTab("about:blank", async () => {
51+
await openContextMenu();
52+
await TestUtils.waitForCondition(
53+
() =>
54+
document.getElementById("context-ask-chat").getItemAtIndex(0)?.label ==
55+
"Summarize Page",
56+
"page prompt added"
57+
);
58+
document.getElementById("context-ask-chat").getItemAtIndex(0).click();
59+
await hideContextMenu();
60+
});
61+
62+
Assert.equal(stub.callCount, 1, "one menu prompt");
63+
Assert.equal(stub.firstCall.args[0].id, "summarize", "summarize prompt");
64+
Assert.ok(stub.firstCall.args[0].badge, "new badge");
65+
66+
sandbox.restore();
67+
SidebarController.hide();
68+
});
69+
1170
/**
1271
* Check badge toggle by prefs
1372
*/

browser/locales/en-US/browser/genai.ftl

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,30 @@ genai-prompts-proofread =
4646
4747
## Chatbot menu shortcuts
4848

49+
genai-menu-no-provider =
50+
.label = Ask an AI Chatbot
51+
genai-menu-choose-chatbot =
52+
.label = Choose an AI Chatbot
4953
genai-menu-ask-generic =
50-
.label = Ask AI chatbot
54+
.label = Ask AI Chatbot
5155
# $provider (string) - name of the provider
5256
genai-menu-ask-provider =
5357
.label = Ask { $provider }
58+
genai-menu-open-generic =
59+
.label = Open AI Chatbot
60+
# $provider (string) - name of the provider
61+
genai-menu-open-provider =
62+
.label = Open { $provider }
5463
genai-menu-remove-generic =
55-
.label = Remove AI chatbot
64+
.label = Remove AI Chatbot
5665
# $provider (string) - name of the provider
5766
genai-menu-remove-provider =
5867
.label = Remove { $provider }
68+
genai-menu-remove-sidebar =
69+
.label = Remove from Sidebar
70+
71+
genai-menu-new-badge = New
72+
genai-menu-summarize-page = Summarize Page
5973
6074
genai-input-ask-generic =
6175
.placeholder = Ask AI chatbot…
@@ -105,6 +119,10 @@ genai-options-hide-shortcut =
105119
genai-options-about-chatbot =
106120
.label = About AI chatbots in { -brand-short-name }
107121
122+
## Chatbot footer
123+
124+
genai-page-button-summarize = Summarize page
125+
108126
## Chatbot onboarding
109127

110128
genai-chatbot-contextual-title = Use an AI chatbot without switching tabs

0 commit comments

Comments
 (0)