Files
tubestation/browser/components/genai/LinkPreview.sys.mjs
Ed Lee 01ab2e3de0 Bug 1943394 - Hook up model to generate preview content r=firefox-ai-ml-reviewers,atossou,txia
Introduce some text pre/post-processing and mostly hardcoded engine config in a new module. Allow some pref configuration.

Differential Revision: https://phabricator.services.mozilla.com/D241268
2025-03-13 00:35:01 +00:00

189 lines
5.5 KiB
JavaScript

/* eslint-disable no-console */
/**
* 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 = {};
ChromeUtils.defineESModuleGetters(lazy, {
LinkPreviewModel:
"moz-src:///browser/components/genai/LinkPreviewModel.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gLinkPreviewEnabled",
"browser.ml.linkPreview.enabled",
false,
(_pref, _old, val) => LinkPreview.onEnabledPref(val)
);
export const LinkPreview = {
keyboardComboActive: false,
_windowStates: new Map(),
/**
* Handles the preference change for enabling/disabling Link Preview.
* It adds or removes event listeners for all tracked windows based on the new preference value.
*
* @param {boolean} enabled - The new state of the Link Preview preference.
*/
onEnabledPref(enabled) {
const method = enabled ? "_addEventListeners" : "_removeEventListeners";
for (const win of this._windowStates.keys()) {
this[method](win);
}
},
/**
* Handles startup tasks such as telemetry and adding listeners.
*
* @param {Window} win - The window context used to add event listeners.
*/
init(win) {
this._windowStates.set(win, {});
if (lazy.gLinkPreviewEnabled) {
this._addEventListeners(win);
}
},
/**
* Teardown the Link Preview feature for the given window.
* Removes event listeners from the specified window and removes it from the window map.
*
* @param {Window} win - The window context to uninitialize.
*/
teardown(win) {
// Remove event listeners from the specified window
if (lazy.gLinkPreviewEnabled) {
this._removeEventListeners(win);
}
// Remove the window from the map
this._windowStates.delete(win);
},
/**
* Adds all needed event listeners and updates the state.
*
* @param {Window} win - The window to which event listeners are added.
*/
_addEventListeners(win) {
win.addEventListener("OverLink", this, true);
win.addEventListener("keydown", this, true);
win.addEventListener("keyup", this, true);
},
/**
* Removes all event listeners and updates the state.
*
* @param {Window} win - The window from which event listeners are removed.
*/
_removeEventListeners(win) {
win.removeEventListener("OverLink", this, true);
win.removeEventListener("keydown", this, true);
win.removeEventListener("keyup", this, true);
},
/**
* Handles keyboard events ("keydown" and "keyup") for the Link Preview feature.
* Adjusts the state of keyboardComboActive based on modifier keys.
*
* @param {KeyboardEvent} event - The keyboard event to be processed.
*/
handleEvent(event) {
switch (event.type) {
case "keydown":
this._onKeyDown(event);
break;
case "keyup":
this._onKeyUp(event);
break;
case "OverLink":
this._onLinkPreview(event);
break;
default:
break;
}
},
/**
* Handles "keydown" events.
*
* @param {KeyboardEvent} event - The keyboard event to be processed.
*/
_onKeyDown(event) {
const win = event.currentTarget;
if (event.altKey) {
if (!this.keyboardComboActive) {
this.keyboardComboActive = true;
this._maybeLinkPreview(win);
}
}
},
/**
* Handles "keyup" events.
*
* @param {KeyboardEvent} event - The keyboard event to be processed.
*/
_onKeyUp(event) {
const win = event.currentTarget;
// Clear the flag when the Alt key is released.
if (!event.altKey) {
if (this.keyboardComboActive) {
this.keyboardComboActive = false;
this._maybeLinkPreview(win);
}
}
},
/**
* Handles "OverLink" events.
* Stores the hovered link URL in the per-window state object and processes the
* link preview if the keyboard combination is active.
*
* @param {CustomEvent} event - The event object containing details about the link preview.
*/
_onLinkPreview(event) {
const win = event.currentTarget;
const url = event.detail.url;
// Store the current overLink in the per-window state object.
const stateObject = this._windowStates.get(win);
stateObject.overLink = url;
if (this.keyboardComboActive) {
this._maybeLinkPreview(win);
}
},
/**
* Determines whether to process or cancel the link preview based on the current state.
* If a URL is available and the keyboard combination is active, it processes the link preview.
* Otherwise, it cancels the link preview.
*
* @param {Window} win - The window context in which the link preview may occur.
*/
async _maybeLinkPreview(win) {
const stateObject = this._windowStates.get(win);
const url = stateObject.overLink;
if (url && this.keyboardComboActive) {
console.log(`Previewing link: ${url}`);
const browsingContext = win.browsingContext;
const actor = browsingContext.currentWindowGlobal.getActor("LinkPreview");
//TODO: use result from sendQuery below for link preview rendering
//TODO: figure out how to get read duration data from Reader mode
const result = await actor.fetchPageData(url);
console.log(result);
console.log("Generating text AI...");
lazy.LinkPreviewModel.generateTextAI(result.article.textContent, {
onError: console.error,
onText: console.log,
});
}
},
};