Merge pull request #20210 from calixteman/comment_sidebar

[Editor] Add a sidebar allowing the user to navigate between the comments in a pdf (bug 1985567)
This commit is contained in:
calixteman 2025-08-30 14:19:04 +02:00 committed by GitHub
commit 9e2e9e2096
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 767 additions and 8 deletions

View File

@ -328,6 +328,10 @@ pdfjs-comment-floating-button =
.title = Comment
.aria-label = Comment
pdfjs-comment-floating-button-label = Comment
pdfjs-editor-comment-button =
.title = Comment
.aria-label = Comment
pdfjs-editor-comment-button-label = Comment
pdfjs-editor-signature-button =
.title = Add signature
pdfjs-editor-signature-button-label = Add signature
@ -395,6 +399,23 @@ pdfjs-free-text2 =
.aria-label = Text Editor
.default-content = Start typing…
# Used to show how many comments are present in the pdf file.
# Variables:
# $count (Number) - the number of comments.
pdfjs-editor-comments-sidebar-title =
{ $count ->
[one] Comment
*[other] Comments
}
pdfjs-editor-comments-sidebar-close-button =
.title = Close the sidebar
.aria-label = Close the sidebar
pdfjs-editor-comments-sidebar-close-button-label = Close the sidebar
# Instructional copy to add a comment by selecting text or an annotations.
pdfjs-editor-comments-sidebar-no-comments = Add a comment by selecting text or an annotation.
## Alt-text dialog
# Alternative text (alt text) helps when people can't see the image.

View File

@ -585,6 +585,8 @@ class AnnotationEditorUIManager {
#activeEditor = null;
#allEditableAnnotations = null;
#allEditors = new Map();
#allLayers = new Map();
@ -663,6 +665,8 @@ class AnnotationEditorUIManager {
#showAllStates = null;
#pdfDocument = null;
#previousStates = {
isEditing: false,
isEmpty: true,
@ -846,6 +850,7 @@ class AnnotationEditorUIManager {
this.#altTextManager = altTextManager;
this.#commentManager = commentManager;
this.#signatureManager = signatureManager;
this.#pdfDocument = pdfDocument;
this._eventBus = eventBus;
eventBus._on("editingaction", this.onEditingAction.bind(this), { signal });
eventBus._on("pagechanging", this.onPageChanging.bind(this), { signal });
@ -928,6 +933,7 @@ class AnnotationEditorUIManager {
this.#floatingToolbar = null;
this.#mainHighlightColorPicker?.destroy();
this.#mainHighlightColorPicker = null;
this.#allEditableAnnotations = null;
if (this.#focusMainContainerTimeoutId) {
clearTimeout(this.#focusMainContainerTimeoutId);
this.#focusMainContainerTimeoutId = null;
@ -937,6 +943,7 @@ class AnnotationEditorUIManager {
this.#translationTimeoutId = null;
}
this._editorUndoBar?.destroy();
this.#pdfDocument = null;
}
combinedSignal(ac) {
@ -1790,6 +1797,10 @@ class AnnotationEditorUIManager {
this.#updateModeCapability = Promise.withResolvers();
this.#currentDrawingSession?.commitOrRemove();
if (this.#mode === AnnotationEditorType.POPUP) {
this.#commentManager?.hideSidebar();
}
this.#mode = mode;
if (mode === AnnotationEditorType.NONE) {
this.setEditingState(false);
@ -1800,9 +1811,18 @@ class AnnotationEditorUIManager {
this.#updateModeCapability.resolve();
return;
}
if (mode === AnnotationEditorType.SIGNATURE) {
await this.#signatureManager?.loadSignatures();
}
if (mode === AnnotationEditorType.POPUP) {
this.#allEditableAnnotations ||=
await this.#pdfDocument.getAnnotationsByType(
new Set(this.#editorTypes.map(editorClass => editorClass._editorType))
);
this.#commentManager?.showSidebar(this.#allEditableAnnotations);
}
this.setEditingState(true);
await this.#enableAll();
this.unselectAll();

View File

@ -75,6 +75,7 @@ const AnnotationEditorType = {
HIGHLIGHT: 9,
STAMP: 13,
INK: 15,
POPUP: 16,
SIGNATURE: 101,
COMMENT: 102,
};

View File

@ -493,7 +493,30 @@ const PDFViewerApplication = {
: null;
const commentManager =
AppOptions.get("enableComment") && appConfig.editCommentDialog
? new CommentManager(appConfig.editCommentDialog, overlayManager)
? new CommentManager(
appConfig.editCommentDialog,
{
sidebar:
appConfig.annotationEditorParams?.editorCommentsSidebar || null,
commentsList:
appConfig.annotationEditorParams?.editorCommentsSidebarList ||
null,
commentCount:
appConfig.annotationEditorParams?.editorCommentsSidebarCount ||
null,
sidebarTitle:
appConfig.annotationEditorParams?.editorCommentsSidebarTitle ||
null,
closeButton:
appConfig.annotationEditorParams
?.editorCommentsSidebarCloseButton || null,
commentToolbarButton:
appConfig.toolbar?.editorCommentButton || null,
},
eventBus,
linkService,
overlayManager
)
: null;
const enableHWA = AppOptions.get("enableHWA"),
@ -589,6 +612,10 @@ const PDFViewerApplication = {
if (editorSignatureButton && AppOptions.get("enableSignatureEditor")) {
editorSignatureButton.parentElement.hidden = false;
}
const editorCommentButton = appConfig.toolbar?.editorCommentButton;
if (editorCommentButton && AppOptions.get("enableComment")) {
editorCommentButton.parentElement.hidden = false;
}
this.annotationEditorParams = new AnnotationEditorParams(
appConfig.annotationEditorParams,
eventBus

View File

@ -304,6 +304,10 @@
box-sizing: border-box;
pointer-events: auto;
&:dir(rtl) {
border-radius: 6px 6px 0;
}
&::before {
content: "";
display: inline-block;
@ -315,6 +319,7 @@
background-color: var(--comment-button-fg);
margin: 0;
padding: 0;
transform: scaleX(var(--dir-factor));
}
&:focus-visible {
@ -351,3 +356,241 @@
}
}
}
#editorCommentsSidebar {
--comment-close-button-icon: url(images/comment-closeButton.svg);
--comment-date-fg-color: light-dark(
rgb(21 20 26 / 0.69),
rgb(251 251 254 / 0.69)
);
--comment-bg-color: light-dark(#f9f9fb, #1c1b22);
--comment-hover-bg-color: light-dark(
rgb(21 20 26 / 0.14),
rgb(251 251 254 / 0.14)
);
--comment-active-bg-color: light-dark(
rgb(21 20 26 / 0.21),
rgb(251 251 254 / 0.21)
);
--comment-border-color: light-dark(#f0f0f4, #52525e);
--comment-focus-outline-color: light-dark(#0062fa, #00cadb);
--comment-fg-color: light-dark(#15141a, #fbfbfe);
--comment-count-bg-color: light-dark(#e2f7ff, #00317e);
--comment-indicator-active-fg-color: light-dark(#0041a4, #a6ecf4);
--comment-indicator-focus-fg-color: light-dark(#5b5b66, #fbfbfe);
--comment-indicator-hover-fg-color: light-dark(#0053cb, #61dce9);
--comment-indicator-selected-fg-color: light-dark(#0062fa, #00cadb);
@media screen and (forced-colors: active) {
--comment-date-fg-color: CanvasText;
--comment-bg-color: Canvas;
--comment-hover-bg-color: SelectedItemText;
--comment-active-bg-color: SelectedItemText;
--comment-border-color: CanvasText;
--comment-fg-color: CanvasText;
--comment-count-bg-color: Canvas;
--comment-indicator-active-fg-color: SelectedItem;
--comment-indicator-focus-fg-color: CanvasText;
--comment-indicator-hover-fg-color: CanvasText;
--comment-indicator-selected-fg-color: SelectedItem;
}
display: flex;
width: 239px;
height: auto;
min-width: 180px;
max-width: 632px;
padding-bottom: 16px;
flex-direction: column;
align-items: flex-start;
#editorCommentsSidebarHeader {
width: 100%;
box-sizing: border-box;
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
.commentCount {
display: flex;
align-items: baseline;
gap: 6px;
user-select: none;
#editorCommentsSidebarTitle {
font: menu;
font-style: normal;
font-weight: 590;
line-height: normal;
font-size: 17px;
color: var(--comment-fg-color);
}
#editorCommentsSidebarCount {
padding: 0 4px;
border-radius: 4px;
background-color: var(--comment-count-bg-color);
color: var(--comment-fg-color);
text-align: center;
font: menu;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
}
#editorCommentsSidebarCloseButton {
width: 32px;
height: 32px;
padding: 8px;
border: none;
background: none;
cursor: pointer;
&::before {
content: "";
display: inline-block;
width: 100%;
height: 100%;
mask-repeat: no-repeat;
mask-position: center;
mask-image: var(--comment-close-button-icon);
background-color: var(--comment-fg-color);
}
&:hover {
background-color: var(--comment-hover-bg-color);
}
&:active {
background-color: var(--comment-active-bg-color);
}
> span {
display: inline-block;
width: 0;
height: 0;
overflow: hidden;
}
}
}
#editorCommentsSidebarList {
display: flex;
width: auto;
padding: 1px 16px 0;
gap: 10px;
flex: 1 0 0;
align-self: stretch;
align-items: flex-start;
flex-direction: column;
list-style-type: none;
.sidebarComment {
display: flex;
width: auto;
padding: 8px 16px 16px;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
gap: 4px;
border-radius: 8px;
border: 0.5px solid var(--comment-border-color);
background-color: var(--comment-bg-color);
&:not(.noComments) {
&:hover {
background-color: var(--comment-hover-bg-color);
time::after {
display: inline-block;
background-color: var(--comment-indicator-hover-fg-color);
}
}
&:active {
background-color: var(--comment-active-bg-color);
time::after {
display: inline-block;
background-color: var(--comment-indicator-active-fg-color);
}
}
&:is(:focus, :focus-visible) time::after {
display: inline-block;
background-color: var(--comment-indicator-focus-fg-color);
}
&:focus-visible {
outline: 2px solid var(--comment-focus-outline-color);
outline-offset: 2px;
}
&.selected {
.sidebarCommentText {
max-height: fit-content;
-webkit-line-clamp: unset;
}
time::after {
display: inline-block;
background-color: var(--comment-indicator-selected-fg-color);
}
}
}
.sidebarCommentText {
font: menu;
font-style: normal;
font-weight: 400;
line-height: normal;
font-size: 15px;
width: 100%;
height: fit-content;
max-height: 80px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
overflow-wrap: break-word;
}
&.noComments .sidebarCommentText {
max-height: fit-content;
-webkit-line-clamp: unset;
user-select: none;
}
time {
width: 100%;
display: inline-flex;
align-items: center;
justify-content: space-between;
font: menu;
font-style: normal;
font-weight: 400;
line-height: normal;
font-size: 13px;
&::after {
content: "";
display: none;
width: 16px;
height: 16px;
mask-repeat: no-repeat;
mask-position: center;
mask-image: var(--comment-edit-button-icon);
transform: scaleX(var(--dir-factor));
}
}
}
}
}

View File

@ -13,7 +13,15 @@
* limitations under the License.
*/
import { getRGB, noContextMenu, shadow, stopEvent } from "pdfjs-lib";
import {
AnnotationEditorType,
getRGB,
noContextMenu,
PDFDateString,
shadow,
stopEvent,
} from "pdfjs-lib";
import { binarySearchFirstItem } from "./ui_utils.js";
class CommentManager {
#actions;
@ -40,6 +48,8 @@ class CommentManager {
#saveButton;
#sidebar;
#uiManager;
#prevDragX = Infinity;
@ -66,6 +76,9 @@ class CommentManager {
cancelButton,
saveButton,
},
sidebar,
eventBus,
linkService,
overlayManager
) {
this.#actions = actions;
@ -73,6 +86,7 @@ class CommentManager {
this.#editMenuItem = editMenuItem;
this.#deleteMenuItem = deleteMenuItem;
this.#menu = menu;
this.#sidebar = new CommentSidebar(sidebar, eventBus, linkService);
this.#textInput = textInput;
this.#textView = textView;
this.#overlayManager = overlayManager;
@ -158,6 +172,26 @@ class CommentManager {
overlayManager.register(dialog);
}
showSidebar(annotations) {
this.#sidebar.show(annotations);
}
hideSidebar() {
this.#sidebar.hide();
}
removeComments(ids) {
this.#sidebar.removeComments(ids);
}
selectComment(id) {
this.#sidebar.selectComment(null, id);
}
addComment(annotation) {
this.#sidebar.addComment(annotation);
}
#closeMenu() {
if (!this.#menuAC) {
return;
@ -378,4 +412,317 @@ class CommentManager {
}
}
class CommentSidebar {
#annotations = null;
#boundCommentClick = this.#commentClick.bind(this);
#boundCommentKeydown = this.#commentKeydown.bind(this);
#sidebar;
#closeButton;
#commentsList;
#commentCount;
#sidebarTitle;
#linkService;
#elementsToAnnotations = null;
#idsToElements = null;
constructor(
{
sidebar,
commentsList,
commentCount,
sidebarTitle,
closeButton,
commentToolbarButton,
},
eventBus,
linkService
) {
this.#sidebar = sidebar;
this.#sidebarTitle = sidebarTitle;
this.#commentsList = commentsList;
this.#commentCount = commentCount;
this.#linkService = linkService;
this.#closeButton = closeButton;
closeButton.addEventListener("click", () => {
eventBus.dispatch("switchannotationeditormode", {
source: this,
mode: AnnotationEditorType.NONE,
});
});
commentToolbarButton.addEventListener("keydown", e => {
if (e.key === "ArrowDown" || e.key === "Home" || e.key === "F6") {
this.#commentsList.firstElementChild.focus();
stopEvent(e);
} else if (e.key === "ArrowUp" || e.key === "End") {
this.#commentsList.lastElementChild.focus();
stopEvent(e);
}
});
this.#sidebar.hidden = true;
}
show(annotations) {
this.#elementsToAnnotations = new WeakMap();
this.#idsToElements = new Map();
this.#annotations = annotations = annotations.filter(
a => a.popupRef && a.contentsObj?.str
);
annotations.sort(this.#sortComments.bind(this));
if (annotations.length !== 0) {
const fragment = document.createDocumentFragment();
for (const annotation of annotations) {
fragment.append(this.#createCommentElement(annotation));
}
this.#setCommentsCount(fragment);
this.#commentsList.append(fragment);
} else {
this.#setCommentsCount();
}
this.#sidebar.hidden = false;
}
hide() {
this.#sidebar.hidden = true;
this.#commentsList.replaceChildren();
this.#elementsToAnnotations = null;
this.#idsToElements = null;
this.#annotations = null;
}
removeComments(ids) {
if (ids.length === 0) {
return;
}
if (
new Set(this.#idsToElements.keys()).difference(new Set(ids)).size === 0
) {
this.#removeAll();
return;
}
for (const id of ids) {
this.#removeComment(id);
}
}
focusComment(id) {
const element = this.#idsToElements.get(id);
if (!element) {
return;
}
this.#sidebar.scrollTop = element.offsetTop - this.#sidebar.offsetTop;
for (const el of this.#commentsList.children) {
el.classList.toggle("selected", el === element);
}
}
#removeComment(id) {
const element = this.#idsToElements.get(id);
if (!element) {
return;
}
const annotation = this.#elementsToAnnotations.get(element);
const index = binarySearchFirstItem(
this.#annotations,
a => this.#sortComments(a, annotation) >= 0
);
if (index >= this.#annotations.length) {
return;
}
this.#annotations.splice(index, 1);
element.remove();
this.#idsToElements.delete(id);
this.#setCommentsCount();
}
#removeAll() {
this.#commentsList.replaceChildren();
this.#elementsToAnnotations = new WeakMap();
this.#idsToElements.clear();
this.#annotations.length = 0;
this.#setCommentsCount();
}
selectComment(element, id = null) {
element ||= this.#idsToElements.get(id);
for (const el of this.#commentsList.children) {
el.classList.toggle("selected", el === element);
}
}
addComment(annotation) {
if (this.#idsToElements.has(annotation.id)) {
return;
}
const { popupRef, contentsObj } = annotation;
if (!popupRef || !contentsObj?.str) {
return;
}
const commentItem = this.#createCommentElement(annotation);
if (this.#annotations.length === 0) {
this.#commentsList.replaceChildren(commentItem);
this.#annotations.push(annotation);
this.#setCommentsCount();
return;
}
const index = binarySearchFirstItem(
this.#annotations,
a => this.#sortComments(a, annotation) >= 0
);
this.#annotations.splice(index, 0, annotation);
if (index >= this.#commentsList.children.length) {
this.#commentsList.append(commentItem);
} else {
this.#commentsList.insertBefore(
commentItem,
this.#commentsList.children[index]
);
}
this.#setCommentsCount();
}
#setCommentsCount(container = this.#commentsList) {
const count = this.#idsToElements.size;
this.#sidebarTitle.setAttribute(
"data-l10n-args",
JSON.stringify({ count })
);
this.#commentCount.textContent = count;
if (count === 0) {
container.append(this.#createZeroCommentElement());
}
}
#createZeroCommentElement() {
const commentItem = document.createElement("li");
commentItem.classList.add("sidebarComment", "noComments");
commentItem.role = "button";
const textDiv = document.createElement("div");
textDiv.className = "sidebarCommentText";
textDiv.setAttribute(
"data-l10n-id",
"pdfjs-editor-comments-sidebar-no-comments"
);
commentItem.addEventListener("keydown", this.#boundCommentKeydown);
commentItem.append(textDiv);
return commentItem;
}
#createCommentElement(annotation) {
const {
creationDate,
modificationDate,
contentsObj: { str: text },
} = annotation;
const commentItem = document.createElement("li");
commentItem.role = "button";
commentItem.className = "sidebarComment";
commentItem.tabIndex = -1;
const dateDiv = document.createElement("time");
const date = PDFDateString.toDateObject(modificationDate || creationDate);
dateDiv.dateTime = date.toISOString();
const dateFormat = new Intl.DateTimeFormat(undefined, {
dateStyle: "long",
});
dateDiv.textContent = dateFormat.format(date);
const textDiv = document.createElement("div");
textDiv.className = "sidebarCommentText";
textDiv.textContent = text;
commentItem.append(dateDiv, textDiv);
commentItem.addEventListener("click", this.#boundCommentClick);
commentItem.addEventListener("keydown", this.#boundCommentKeydown);
this.#elementsToAnnotations.set(commentItem, annotation);
this.#idsToElements.set(annotation.id, commentItem);
return commentItem;
}
#commentClick({ currentTarget }) {
if (currentTarget.classList.contains("selected")) {
return;
}
const annotation = this.#elementsToAnnotations.get(currentTarget);
if (!annotation) {
return;
}
const { pageIndex, rect } = annotation;
const SPACE_ABOVE_ANNOTATION = 10;
this.#linkService?.goToXY(
pageIndex + 1,
rect[0],
rect[3] + SPACE_ABOVE_ANNOTATION
);
this.selectComment(currentTarget);
}
#commentKeydown(e) {
const { key, currentTarget } = e;
switch (key) {
case "ArrowDown":
(
currentTarget.nextElementSibling ||
this.#commentsList.firstElementChild
).focus();
stopEvent(e);
break;
case "ArrowUp":
(
currentTarget.previousElementSibling ||
this.#commentsList.lastElementChild
).focus();
stopEvent(e);
break;
case "Home":
this.#commentsList.firstElementChild.focus();
stopEvent(e);
break;
case "End":
this.#commentsList.lastElementChild.focus();
stopEvent(e);
break;
case "Enter":
case " ":
this.#commentClick(e);
stopEvent(e);
break;
case "ShiftTab":
this.#closeButton.focus();
stopEvent(e);
break;
}
}
#sortComments(a, b) {
if (a.pageIndex !== b.pageIndex) {
return a.pageIndex - b.pageIndex;
}
if (a.rect[3] !== b.rect[3]) {
return b.rect[3] - a.rect[3];
}
if (a.rect[0] !== b.rect[0]) {
return a.rect[0] - b.rect[0];
}
if (a.rect[1] !== b.rect[1]) {
return b.rect[1] - a.rect[1];
}
if (a.rect[2] !== b.rect[2]) {
return a.rect[2] - b.rect[2];
}
return a.id.localeCompare(b.id);
}
}
export { CommentManager };

View File

@ -19,6 +19,7 @@
@import url(xfa_layer_builder.css);
/* Ignored in GECKOVIEW builds: */
@import url(annotation_editor_layer_builder.css);
@import url(sidebar.css);
:root {
color-scheme: light dark;

37
web/sidebar.css Normal file
View File

@ -0,0 +1,37 @@
/* Copyright 2025 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.sidebar {
--sidebar-bg-color: light-dark(#fff, #23222b);
--sidebar-border-color: light-dark(
rgb(21 20 26 / 0.1),
rgb(251 251 254 / 0.1)
);
--sidebar-box-shadow:
0 0.25px 0.75px light-dark(rgb(0 0 0 / 0.05), rgb(0 0 0 / 0.2)),
0 2px 6px 0 light-dark(rgb(0 0 0 / 0.1), rgb(0 0 0 / 0.4));
@media screen and (forced-colors: active) {
--sidebar-bg-color: Canvas;
--sidebar-border-color: CanvasText;
--sidebar-box-shadow: none;
}
border-radius: 8px;
box-shadow: var(--sidebar-box-shadow);
border: 1px solid var(--sidebar-border-color);
background-color: var(--sidebar-bg-color);
inset-block-start: calc(100% + var(--doorhanger-height) - 2px);
}

View File

@ -67,6 +67,18 @@ class Toolbar {
{ element: options.zoomOut, eventName: "zoomout" },
{ element: options.print, eventName: "print" },
{ element: options.download, eventName: "download" },
{
element: options.editorCommentButton,
eventName: "switchannotationeditormode",
eventDetails: {
get mode() {
const { classList } = options.editorCommentButton;
return classList.contains("toggled")
? AnnotationEditorType.NONE
: AnnotationEditorType.POPUP;
},
},
},
{
element: options.editorFreeTextButton,
eventName: "switchannotationeditormode",
@ -278,6 +290,8 @@ class Toolbar {
#editorModeChanged({ mode }) {
const {
editorCommentButton,
editorCommentParamsToolbar,
editorFreeTextButton,
editorFreeTextParamsToolbar,
editorHighlightButton,
@ -290,6 +304,11 @@ class Toolbar {
editorSignatureParamsToolbar,
} = this.#opts;
toggleExpandedBtn(
editorCommentButton,
mode === AnnotationEditorType.POPUP,
editorCommentParamsToolbar
);
toggleExpandedBtn(
editorFreeTextButton,
mode === AnnotationEditorType.FREETEXT,
@ -316,12 +335,13 @@ class Toolbar {
editorSignatureParamsToolbar
);
const isDisable = mode === AnnotationEditorType.DISABLE;
editorFreeTextButton.disabled = isDisable;
editorHighlightButton.disabled = isDisable;
editorInkButton.disabled = isDisable;
editorStampButton.disabled = isDisable;
editorSignatureButton.disabled = isDisable;
editorCommentButton.disabled =
editorFreeTextButton.disabled =
editorHighlightButton.disabled =
editorInkButton.disabled =
editorStampButton.disabled =
editorSignatureButton.disabled =
mode === AnnotationEditorType.DISABLE;
}
#updateUIState(resetNumPages = false) {

View File

@ -99,6 +99,7 @@
--loading-icon: url(images/loading.svg);
--treeitem-expanded-icon: url(images/treeitem-expanded.svg);
--treeitem-collapsed-icon: url(images/treeitem-collapsed.svg);
--toolbarButton-editorComment-icon: url(images/comment-editButton.svg);
--toolbarButton-editorFreeText-icon: url(images/toolbarButton-editorFreeText.svg);
--toolbarButton-editorHighlight-icon: url(images/toolbarButton-editorHighlight.svg);
--toolbarButton-editorInk-icon: url(images/toolbarButton-editorInk.svg);
@ -541,6 +542,11 @@ body {
mask-image: var(--toolbarButton-zoomIn-icon);
}
#editorCommentButton::before {
mask-image: var(--toolbarButton-editorComment-icon);
transform: scaleX(var(--dir-factor));
}
#editorFreeTextButton::before {
mask-image: var(--toolbarButton-editorFreeText-icon);
}

View File

@ -245,6 +245,25 @@ See https://github.com/adobe-type-tools/cmap-resources
</div>
<div id="toolbarViewerRight" class="toolbarHorizontalGroup">
<div id="editorModeButtons" class="toolbarHorizontalGroup" role="radiogroup">
<div id="editorComment" class="toolbarButtonWithContainer" hidden="true">
<button id="editorCommentButton" class="toolbarButton" type="button" tabindex="0" disabled="disabled" role="radio" aria-expanded="false" aria-haspopup="true" aria-controls="editorCommentParamsToolbar" data-l10n-id="pdfjs-editor-comment-button">
<span data-l10n-id="pdfjs-editor-comment-button-label"></span>
</button>
<div class="editorParamsToolbar sidebar hidden menu" id="editorCommentParamsToolbar">
<div id="editorCommentsSidebar" class="menuContainer" role="landmark" aria-labelledby="editorCommentsSidebarHeader">
<div id="editorCommentsSidebarHeader" role="heading" aria-level="2">
<span class="commentCount">
<span id="editorCommentsSidebarTitle" data-l10n-id="pdfjs-editor-comments-sidebar-title" data-l10n-args='{ "count": 0 }'></span>
<span id="editorCommentsSidebarCount"></span>
</span>
<button id="editorCommentsSidebarCloseButton" type="button" tabindex="0" data-l10n-id="pdfjs-editor-comments-sidebar-close-button">
<span data-l10n-id="pdfjs-editor-comments-sidebar-close-button-label"></span>
</button>
</div>
<ul id="editorCommentsSidebarList"></ul>
</div>
</div>
</div>
<div id="editorSignature" class="toolbarButtonWithContainer" hidden="true">
<button id="editorSignatureButton" class="toolbarButton" type="button" tabindex="0" disabled="disabled" role="radio" aria-expanded="false" aria-haspopup="true" aria-controls="editorSignatureParamsToolbar" data-l10n-id="pdfjs-editor-signature-button">
<span data-l10n-id="pdfjs-editor-signature-button-label"></span>

View File

@ -45,6 +45,10 @@ function getViewerConfiguration() {
zoomIn: document.getElementById("zoomInButton"),
zoomOut: document.getElementById("zoomOutButton"),
print: document.getElementById("printButton"),
editorCommentButton: document.getElementById("editorCommentButton"),
editorCommentParamsToolbar: document.getElementById(
"editorCommentParamsToolbar"
),
editorFreeTextButton: document.getElementById("editorFreeTextButton"),
editorFreeTextParamsToolbar: document.getElementById(
"editorFreeTextParamsToolbar"
@ -241,6 +245,19 @@ function getViewerConfiguration() {
updateButton: document.getElementById("editSignatureUpdateButton"),
},
annotationEditorParams: {
editorCommentsSidebar: document.getElementById("editorCommentsSidebar"),
editorCommentsSidebarCount: document.getElementById(
"editorCommentsSidebarCount"
),
editorCommentsSidebarTitle: document.getElementById(
"editorCommentsSidebarTitle"
),
editorCommentsSidebarCloseButton: document.getElementById(
"editorCommentsSidebarCloseButton"
),
editorCommentsSidebarList: document.getElementById(
"editorCommentsSidebarList"
),
editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
editorFreeTextColor: document.getElementById("editorFreeTextColor"),
editorInkColor: document.getElementById("editorInkColor"),