diff --git a/l10n/en-US/viewer.ftl b/l10n/en-US/viewer.ftl index 2f7d94b92..df44ab339 100644 --- a/l10n/en-US/viewer.ftl +++ b/l10n/en-US/viewer.ftl @@ -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. diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index e39006d71..b054d1889 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -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(); diff --git a/src/shared/util.js b/src/shared/util.js index d253a40b0..729c1e698 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -75,6 +75,7 @@ const AnnotationEditorType = { HIGHLIGHT: 9, STAMP: 13, INK: 15, + POPUP: 16, SIGNATURE: 101, COMMENT: 102, }; diff --git a/web/app.js b/web/app.js index 14fe84e70..af4ce3d5b 100644 --- a/web/app.js +++ b/web/app.js @@ -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 diff --git a/web/comment_manager.css b/web/comment_manager.css index 180a664dc..59a795912 100644 --- a/web/comment_manager.css +++ b/web/comment_manager.css @@ -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)); + } + } + } + } +} diff --git a/web/comment_manager.js b/web/comment_manager.js index 65907d04d..f330439db 100644 --- a/web/comment_manager.js +++ b/web/comment_manager.js @@ -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 }; diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css index 2a7a6ecfd..7ced08849 100644 --- a/web/pdf_viewer.css +++ b/web/pdf_viewer.css @@ -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; diff --git a/web/sidebar.css b/web/sidebar.css new file mode 100644 index 000000000..2091e0c22 --- /dev/null +++ b/web/sidebar.css @@ -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); +} diff --git a/web/toolbar.js b/web/toolbar.js index 5945cd409..95639af9a 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -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) { diff --git a/web/viewer.css b/web/viewer.css index ec44fa98d..7822771b5 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -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); } diff --git a/web/viewer.html b/web/viewer.html index a57bc06a9..8a98e1215 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -245,6 +245,25 @@ See https://github.com/adobe-type-tools/cmap-resources