Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
GenAI: "resource:///modules/GenAI.sys.mjs",
});
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"providerPref",
"browser.ml.chat.provider",
null,
renderProviders
);
const node = {};
function request(url = lazy.providerPref) {
try {
node.chat.fixupAndLoadURIString(url, {
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
});
} catch (ex) {
console.error("Failed to request chat provider", ex);
}
}
function renderChat() {
const browser = document.createXULElement("browser");
browser.setAttribute("type", "content");
browser.setAttribute("remote", "true");
return document.body.appendChild(browser);
}
async function renderProviders() {
// Skip potential pref change callback when unloading
if ((await document.visibilityState) == "hidden") {
return null;
}
const select = document.getElementById("provider");
select.innerHTML = "";
let selected = false;
const addOption = (text, val) => {
const option = select.appendChild(document.createElement("option"));
option.textContent = text;
option.value = val;
return option;
};
// Add the known providers in order while looking for current selection
lazy.GenAI.chatProviders.forEach((data, url) => {
const option = addOption(data.name, url);
if (lazy.providerPref == url) {
option.selected = true;
selected = true;
} else if (data.hidden) {
option.hidden = true;
}
});
// Must be a custom preference if provider wasn't found
if (!selected) {
const option = addOption(
`Custom provider (${lazy.providerPref})`,
lazy.providerPref
);
option.selected = true;
}
// Update provider telemetry
const providerId = lazy.GenAI.getProviderId(lazy.providerPref);
Glean.genaiChatbot.provider.set(providerId);
if (renderProviders.lastId && document.hasFocus()) {
Glean.genaiChatbot.providerChange.record({
current: providerId,
previous: renderProviders.lastId,
surface: "panel",
});
}
renderProviders.lastId = providerId;
// Load the requested provider
request();
return select;
}
function handleChange({ target }) {
const { value } = target;
switch (target) {
case node.provider:
Services.prefs.setStringPref("browser.ml.chat.provider", value);
break;
}
}
addEventListener("change", handleChange);
// Expose a promise for loading and rendering the chat browser element
var browserPromise = new Promise((resolve, reject) => {
addEventListener("load", async () => {
try {
node.chat = renderChat();
node.provider = await renderProviders();
resolve(node.chat);
} catch (ex) {
console.error("Failed to render on load", ex);
reject(ex);
}
Glean.genaiChatbot.sidebarToggle.record({
opened: true,
provider: lazy.GenAI.getProviderId(),
reason: "load",
});
});
});
addEventListener("unload", () =>
Glean.genaiChatbot.sidebarToggle.record({
opened: false,
provider: lazy.GenAI.getProviderId(),
reason: "unload",
})
);