fune/browser/components/genai/GenAI.sys.mjs

115 lines
3.4 KiB
JavaScript

/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatEnabled",
"browser.ml.chat.enabled"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatPromptPrefix",
"browser.ml.chat.prompt.prefix"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatProvider",
"browser.ml.chat.provider"
);
export const GenAI = {
/**
* Build prompts menu to ask chat for context menu or popup.
*
* @param {MozMenu} menu Element to update
* @param {nsContextMenu} context Additional menu context
*/
buildAskChatMenu(menu, context) {
if (!lazy.chatEnabled || lazy.chatProvider == "") {
context.showItem(menu, false);
return;
}
menu.context = context;
menu.label = "Ask chatbot";
menu.menupopup?.remove();
Services.prefs.getChildList("browser.ml.chat.prompts.").forEach(pref => {
try {
let prompt = Services.prefs.getStringPref(pref);
try {
prompt = JSON.parse(prompt);
} catch (ex) {}
menu.appendItem(prompt.label ?? prompt, prompt.value ?? "");
} catch (ex) {
console.error("Failed to add menu item for " + pref, ex);
}
});
context.showItem(menu, menu.itemCount > 0);
},
/**
* Build a prompt with context.
*
* @param {MozMenuItem} item Use value falling back to label
* @param {object} context Placeholder keys with values to replace
* @returns {string} Prompt with placeholders replaced
*/
buildChatPrompt(item, context = {}) {
// Combine prompt prefix with the item then replace placeholders from the
// original prompt (and not from context)
return (lazy.chatPromptPrefix + (item.value || item.label)).replace(
// Handle %placeholder% as key|options
/\%(\w+)(?:\|([^%]+))?\%/g,
(placeholder, key, options) =>
// Currently only supporting numeric options for slice with `undefined`
// resulting in whole string
context[key]?.slice(0, options) ?? placeholder
);
},
/**
* Handle selected prompt by opening tab or sidebar.
*
* @param {Event} event from menu command
*/
handleAskChat({ target }) {
const win = target.ownerGlobal;
const { selectedTab } = win.gBrowser;
const url = new URL(lazy.chatProvider);
url.searchParams.set(
"q",
this.buildChatPrompt(target, {
currentTabTitle:
(selectedTab._labelIsContentTitle && selectedTab.label) || "",
selection: target.closest("menu").context.selectionInfo.fullText ?? "",
})
);
win.openWebLinkIn(url + "", "tab", { relatedToCurrent: true });
},
/**
* Build preferences for chat such as handling providers.
*
* @param {Window} window for about:preferences
*/
buildPreferences({ document, Preferences }) {
const providerEl = document.getElementById("genai-chat-provider");
if (!providerEl) {
return;
}
const enabled = Preferences.get("browser.ml.chat.enabled");
const onEnabledChange = () => (providerEl.disabled = !enabled.value);
onEnabledChange();
enabled.on("change", onEnabledChange);
// TODO bug 1895433 populate providers
Preferences.add({ id: "browser.ml.chat.provider", type: "string" });
},
};