Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Editor] Implement the new alt text flow (bug 1909604) #18492

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gulpfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ function createWebpackAlias(defines) {
"web-annotation_editor_params": "web/annotation_editor_params.js",
"web-download_manager": "",
"web-external_services": "",
"web-new_alt_text_manager": "web/new_alt_text_manager.js",
"web-null_l10n": "",
"web-pdf_attachment_viewer": "web/pdf_attachment_viewer.js",
"web-pdf_cursor_tools": "web/pdf_cursor_tools.js",
Expand Down Expand Up @@ -1097,6 +1098,7 @@ function buildComponents(defines, dir) {
"web/images/loading-icon.gif",
"web/images/altText_*.svg",
"web/images/editor-toolbar-*.svg",
"web/images/messageBar_*.svg",
"web/images/toolbarButton-{editorHighlight,menuArrow}.svg",
"web/images/cursor-*.svg",
];
Expand Down
49 changes: 49 additions & 0 deletions l10n/en-US/viewer.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,52 @@ pdfjs-editor-colorpicker-red =
pdfjs-editor-highlight-show-all-button-label = Show all
pdfjs-editor-highlight-show-all-button =
.title = Show all

## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.

# Modal header positioned above a text box where users can edit the alt text.
pdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description)

# Modal header positioned above a text box where users can add the alt text.
pdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description)

pdfjs-editor-new-alt-text-textarea =
.placeholder = Write your description here…

# This text refers to the alt text box above this description. It offers a definition of alt text.
pdfjs-editor-new-alt-text-description = Short description for people who can’t see the image or when the image doesn’t load.

# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human.
pdfjs-editor-new-alt-text-disclaimer = This alt text was created automatically.
pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more

pdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically
pdfjs-editor-new-alt-text-not-now-button = Not now
pdfjs-editor-new-alt-text-error-title = Couldn’t create alt text automatically
pdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later.
pdfjs-editor-new-alt-text-error-close-button = Close

# Variables:
# $totalSize (Number) - the total size (in MB) of the AI model.
# $downloadedSize (Number) - the downloaded size (in MB) of the AI model.
# $percent (Number) - the percentage of the downloaded size.
pdfjs-editor-new-alt-text-ai-model-downloading-progress =
.aria-valuemin = 0
.aria-valuemax = { $totalSize }
.aria-valuenow = { $downloadedSize }
.aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)

# This is a button that users can click to edit the alt text they have already added.
pdfjs-editor-new-alt-text-added-button-label = Alt text added

# This is a button that users can click to open the alt text editor and add alt text when it is not present.
pdfjs-editor-new-alt-text-missing-button-label = Missing alt text

# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated.
calixteman marked this conversation as resolved.
Show resolved Hide resolved
pdfjs-editor-new-alt-text-to-review-button-label = Review alt text

# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear.
# Variables:
# $generatedAltText (String) - the generated alt-text.
pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText }
148 changes: 131 additions & 17 deletions src/display/editor/alt_text.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import { noContextMenu } from "../display_utils.js";

class AltText {
#altText = "";
#altText = null;

#altTextDecorative = false;

Expand All @@ -28,12 +28,21 @@ class AltText {

#altTextWasFromKeyBoard = false;

#badge = null;

#editor = null;

#guessedText = null;

#textWithDisclaimer = null;

#useNewAltTextFlow = false;

static _l10nPromise = null;

constructor(editor) {
this.#editor = editor;
this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow;
}

static initialize(l10nPromise) {
Expand All @@ -43,9 +52,17 @@ class AltText {
async render() {
const altText = (this.#altTextButton = document.createElement("button"));
altText.className = "altText";
const msg = await AltText._l10nPromise.get(
"pdfjs-editor-alt-text-button-label"
);
let msg;
if (this.#useNewAltTextFlow) {
altText.classList.add("new");
msg = await AltText._l10nPromise.get(
"pdfjs-editor-new-alt-text-missing-button-label"
);
} else {
msg = await AltText._l10nPromise.get(
"pdfjs-editor-alt-text-button-label"
);
}
altText.textContent = msg;
altText.setAttribute("aria-label", msg);
altText.tabIndex = "0";
Expand Down Expand Up @@ -84,9 +101,62 @@ class AltText {
}

isEmpty() {
if (this.#useNewAltTextFlow) {
return this.#altText === null;
}
return !this.#altText && !this.#altTextDecorative;
}

hasData() {
if (this.#useNewAltTextFlow) {
return this.#altText !== null || !!this.#guessedText;
}
return this.isEmpty();
}

get guessedText() {
return this.#guessedText;
}

async setGuessedText(guessedText) {
if (this.#altText !== null) {
// The user provided their own alt text, so we don't want to overwrite it.
return;
}
this.#guessedText = guessedText;
this.#textWithDisclaimer = await AltText._l10nPromise.get(
"pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer"
)({ generatedAltText: guessedText });
this.#setState();
}

toggleAltTextBadge(visibility = false) {
if (!this.#useNewAltTextFlow || this.#altText) {
this.#badge?.remove();
this.#badge = null;
return;
}
if (!this.#badge) {
const badge = (this.#badge = document.createElement("div"));
badge.className = "noAltTextBadge";
this.#editor.div.append(badge);
}
this.#badge.classList.toggle("hidden", !visibility);
}

serialize(isForCopying) {
let altText = this.#altText;
if (!isForCopying && this.#guessedText === altText) {
altText = this.#textWithDisclaimer;
}
return {
altText,
decorative: this.#altTextDecorative,
guessedText: this.#guessedText,
textWithDisclaimer: this.#textWithDisclaimer,
};
}

get data() {
return {
altText: this.#altText,
Expand All @@ -97,12 +167,24 @@ class AltText {
/**
* Set the alt text data.
*/
set data({ altText, decorative }) {
set data({
altText,
decorative,
guessedText,
textWithDisclaimer,
cancel = false,
}) {
if (guessedText) {
this.#guessedText = guessedText;
this.#textWithDisclaimer = textWithDisclaimer;
}
if (this.#altText === altText && this.#altTextDecorative === decorative) {
return;
}
this.#altText = altText;
this.#altTextDecorative = decorative;
if (!cancel) {
this.#altText = altText;
this.#altTextDecorative = decorative;
}
this.#setState();
}

Expand All @@ -121,25 +203,57 @@ class AltText {
this.#altTextButton?.remove();
this.#altTextButton = null;
this.#altTextTooltip = null;
this.#badge?.remove();
this.#badge = null;
}

async #setState() {
const button = this.#altTextButton;
if (!button) {
return;
}
if (!this.#altText && !this.#altTextDecorative) {
button.classList.remove("done");
this.#altTextTooltip?.remove();
return;

if (this.#useNewAltTextFlow) {
// If we've an alt text, we get an "added".
// 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";
calixteman marked this conversation as resolved.
Show resolved Hide resolved
button.classList.toggle("done", !!this.#altText);
AltText._l10nPromise
.get(`pdfjs-editor-new-alt-text-${type}-button-label`)
.then(msg => {
button.setAttribute("aria-label", msg);
// We can't just use button.textContent here, because it would remove
// the existing tooltip element.
for (const child of button.childNodes) {
if (child.nodeType === Node.TEXT_NODE) {
child.textContent = msg;
break;
}
}
});
if (!this.#altText) {
this.#altTextTooltip?.remove();
return;
}
} else {
if (!this.#altText && !this.#altTextDecorative) {
button.classList.remove("done");
this.#altTextTooltip?.remove();
return;
}
button.classList.add("done");
AltText._l10nPromise
.get("pdfjs-editor-alt-text-edit-button-label")
.then(msg => {
button.setAttribute("aria-label", msg);
});
}
button.classList.add("done");

AltText._l10nPromise
.get("pdfjs-editor-alt-text-edit-button-label")
.then(msg => {
button.setAttribute("aria-label", msg);
});
let tooltip = this.#altTextTooltip;
if (!tooltip) {
this.#altTextTooltip = tooltip = document.createElement("span");
Expand Down
Loading