diff --git a/src/display/editor/alt_text.js b/src/display/editor/alt_text.js index fc346b4d5b51c..3b79f0455a613 100644 --- a/src/display/editor/alt_text.js +++ b/src/display/editor/alt_text.js @@ -75,6 +75,12 @@ class AltText { const onClick = event => { event.preventDefault(); this.#editor._uiManager.editAltText(this.#editor); + if (this.#useNewAltTextFlow) { + this.#editor._reportTelemetry({ + action: "pdfjs.image.alt_text.image_status_label_clicked", + label: this.#label, + }); + } }; altText.addEventListener("click", onClick, { capture: true, signal }); altText.addEventListener( @@ -92,6 +98,14 @@ class AltText { return altText; } + get #label() { + return ( + (this.#altText && "added") || + (this.#altText === null && this.guessedText && "review") || + "missing" + ); + } + finish() { if (!this.#altTextButton) { return; @@ -218,10 +232,13 @@ class AltText { // If we've a guessed text and the alt text has never been set, we get a // "to-review" been set. // Otherwise, we get a "missing". - const type = - (this.#altText && "added") || - (this.#altText === null && this.guessedText && "to-review") || - "missing"; + const label = this.#label; + // TODO: Update the l10n keys to avoid this. + const type = label === "review" ? "to-review" : label; + this.#editor._reportTelemetry({ + action: "pdfjs.image.alt_text.image_status_label_displayed", + label, + }); button.classList.toggle("done", !!this.#altText); AltText._l10nPromise .get(`pdfjs-editor-new-alt-text-${type}-button-label`) diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index fc15ea7270b79..68c6d5407185b 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -104,6 +104,22 @@ class StampEditor extends AnnotationEditor { super.altTextFinish(); } + /** @inheritdoc */ + get telemetryFinalData() { + return { + type: "stamp", + hasAltText: this.hasAltTextData(), + }; + } + + static computeTelemetryFinalData(data) { + const hasAltTextStats = data.get("hasAltText"); + return { + hasAltText: hasAltTextStats.get(true) ?? 0, + hasNoAltText: hasAltTextStats.get(false) ?? 0, + }; + } + #getBitmapFetched(data, fromId = false) { if (!data) { this.remove(); @@ -141,6 +157,10 @@ class StampEditor extends AnnotationEditor { this._uiManager.useNewAltTextFlow && this.#bitmap ) { + this._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { alt_text_modal: false }, + }); try { // The alt-text dialog isn't opened but we still want to guess the alt // text. @@ -247,6 +267,10 @@ class StampEditor extends AnnotationEditor { const data = await this._uiManager.imageManager.getFromFile( input.files[0] ); + this._reportTelemetry({ + action: "pdfjs.image.image_selected", + data: { alt_text_modal: this._uiManager.useNewAltTextFlow }, + }); this.#getBitmapFetched(data); } if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { diff --git a/web/annotation_editor_params.js b/web/annotation_editor_params.js index 22f453a1b9d70..a69206214b3c9 100644 --- a/web/annotation_editor_params.js +++ b/web/annotation_editor_params.js @@ -75,6 +75,13 @@ class AnnotationEditorParams { dispatchEvent("INK_OPACITY", this.valueAsNumber); }); editorStampAddImage.addEventListener("click", () => { + this.eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data: { action: "pdfjs.image.add_image_click" }, + }, + }); dispatchEvent("CREATE"); }); editorFreeHighlightThickness.addEventListener("input", function () { diff --git a/web/new_alt_text_manager.js b/web/new_alt_text_manager.js index e13b47f0fb29a..a123d68c56c34 100644 --- a/web/new_alt_text_manager.js +++ b/web/new_alt_text_manager.js @@ -64,8 +64,6 @@ class NewAltTextManager { #previousAltText = null; - #telemetryData = null; - constructor( { descriptionContainer, @@ -142,6 +140,13 @@ class NewAltTextManager { }); this.#overlayManager.register(dialog); + + this.#learnMore.addEventListener("click", () => { + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.info", + data: { topic: "alt_text" }, + }); + }); } #toggleLoading(value) { @@ -401,6 +406,18 @@ class NewAltTextManager { this.#currentEditor.altTextData = { cancel: true, }; + const altText = this.#textarea.value.trim(); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.dismiss", + data: { + alt_text_type: altText ? "present" : "empty", + flow: this.#firstTime ? "image_add" : "alt_text_edit", + }, + }); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { alt_text_modal: false }, + }); this.#finish(); } @@ -416,13 +433,6 @@ class NewAltTextManager { canvas.width = canvas.height = 0; this.#imageData = null; - this.#currentEditor._reportTelemetry( - this.#telemetryData || { - action: "alt_text_cancel", - } - ); - - this.#telemetryData = null; this.#toggleLoading(false); this.#uiManager?.addEditListeners(); @@ -438,15 +448,33 @@ class NewAltTextManager { altText, decorative: false, }; - this.#telemetryData = { - action: "alt_text_save", - alt_text_description: !!altText, - alt_text_edit: - !!this.#previousAltText && this.#previousAltText !== altText, - alt_text_decorative: false, - alt_text_altered: - this.#guessedAltText && this.#guessedAltText !== altText, - }; + this.#currentEditor.altTextData.guessedAltText = this.#guessedAltText; + + if (this.#guessedAltText && this.#guessedAltText !== altText) { + const guessedWords = new Set(this.#guessedAltText.split(/\s+/)); + const words = new Set(altText.split(/\s+/)); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.user_edit", + data: { + total_words: guessedWords.size, + words_removed: guessedWords.difference(words).size, + words_added: words.difference(guessedWords).size, + }, + }); + } + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { alt_text_modal: true }, + }); + + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.save", + data: { + alt_text_type: altText ? "present" : "empty", + flow: this.#firstTime ? "image_add" : "alt_text_edit", + }, + }); + this.#finish(); } @@ -507,12 +535,21 @@ class ImageAltTextSettings { createModelButton.addEventListener("click", async e => { const checked = this.#togglePref("enableGuessAltText", e); await mlManager.toggleService("altText", checked); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_ai_generation_check", + data: { status: checked }, + }); }); - showAltTextDialogButton.addEventListener( - "click", - this.#togglePref.bind(this, "enableNewAltTextWhenAddingImage") - ); + showAltTextDialogButton.addEventListener("click", e => { + const checked = this.#togglePref("enableNewAltTextWhenAddingImage", e); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_edit_alt_text_check", + data: { status: checked }, + }); + }); deleteModelButton.addEventListener("click", this.#delete.bind(this, true)); downloadModelButton.addEventListener( @@ -522,6 +559,14 @@ class ImageAltTextSettings { closeButton.addEventListener("click", this.#finish.bind(this)); + learnMore.addEventListener("click", () => { + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.info", + data: { topic: "ai_generation" }, + }); + }); + eventBus._on("enablealttextmodeldownload", ({ value }) => { if (value) { this.#download(false); @@ -533,6 +578,16 @@ class ImageAltTextSettings { this.#overlayManager.register(dialog); } + #reportTelemetry(data) { + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data, + }, + }); + } + async #download(isFromUI = false) { if (isFromUI) { this.#downloadModelButton.disabled = true; @@ -589,6 +644,10 @@ class ImageAltTextSettings { ); await this.#overlayManager.open(this.#dialog); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_displayed", + }); } #togglePref(name, { target }) { diff --git a/web/toolbar.js b/web/toolbar.js index b17e0d6cfe7e0..bc4994ee09829 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -112,6 +112,10 @@ class Toolbar { : AnnotationEditorType.STAMP; }, }, + telemetry: { + type: "editing", + data: { action: "pdfjs.image.icon_click" }, + }, }, ]; @@ -173,7 +177,7 @@ class Toolbar { const self = this; // The buttons within the toolbar. - for (const { element, eventName, eventDetails } of buttons) { + for (const { element, eventName, eventDetails, telemetry } of buttons) { element.addEventListener("click", evt => { if (eventName !== null) { eventBus.dispatch(eventName, { @@ -183,6 +187,12 @@ class Toolbar { isFromKeyboard: evt.detail === 0, }); } + if (telemetry) { + eventBus.dispatch("reporttelemetry", { + source: this, + details: telemetry, + }); + } }); } // The non-button elements within the toolbar.