diff options
| author | bobzel <zzzman@gmail.com> | 2025-02-10 19:07:20 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2025-02-10 19:07:20 -0500 |
| commit | c9686eaebffb3547b7e0f20aec64754627af76ce (patch) | |
| tree | 7ebf1c38323a8d7af554ba564acf95cfe79b7709 /src/client/views/pdf | |
| parent | b72d018698ad1d2e713f0fcbef392d23bf1cf545 (diff) | |
| parent | e93ca53af693fa1ec2186ca9417af122bb5e8e09 (diff) | |
updated from master
Diffstat (limited to 'src/client/views/pdf')
| -rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 22 | ||||
| -rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 250 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.scss | 12 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 66 |
4 files changed, 160 insertions, 190 deletions
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 5ab9b556c..11f2f7988 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from '@dash/components'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -131,12 +131,15 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { /** * Creates a GPT drawing based on selected text. */ - gptDraw = async (e: React.PointerEvent) => { + gptDraw = (e: React.PointerEvent) => { try { SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation; runInAction(() => (this._isLoading = true)); - await SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true); - runInAction(() => (this._isLoading = false)); + SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true)?.then( + action(() => { + this._isLoading = false; + }) + ); } catch (err) { console.error(err); } @@ -150,11 +153,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { this.AddDrawingAnnotation(drawing); const docData = drawing[DocData]; docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text; - docData.drawingInput = opts.text; - docData.drawingComplexity = opts.complexity; - docData.drawingColored = opts.autoColor; - docData.drawingSize = opts.size; - docData.drawingData = gptRes; + docData.ai_drawing_input = opts.text; + docData.ai_drawing_complexity = opts.complexity; + docData.ai_drawing_colored = opts.autoColor; + docData.ai_drawing_size = opts.size; + docData.ai_drawing_data = gptRes; + docData.ai = 'gpt'; }); pointerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index d5f5f620c..f5a9f9e6a 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Type } from 'browndash-components'; -import { action, makeObservable, observable } from 'mobx'; +import { Button, IconButton, Toggle, ToggleType, Type } from '@dash/components'; +import { action, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { CgClose, CgCornerUpLeft } from 'react-icons/cg'; @@ -37,10 +37,8 @@ export enum GPTQuizType { MULTIPLE = 2, } -interface GPTPopupProps {} - @observer -export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { +export class GPTPopup extends ObservableReactComponent<object> { // eslint-disable-next-line no-use-before-define static Instance: GPTPopup; private messagesEndRef: React.RefObject<HTMLDivElement>; @@ -48,115 +46,84 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { @observable private chatMode: boolean = false; private correlatedColumns: string[] = []; - @observable - public visible: boolean = false; - @action - public setVisible = (vis: boolean) => { - this.visible = vis; + @observable public Visible: boolean = false; + @action public setVisible = (vis: boolean) => { + this.Visible = vis; }; - @observable - public loading: boolean = false; - @action - public setLoading = (loading: boolean) => { + @observable public loading: boolean = false; + @action public setLoading = (loading: boolean) => { this.loading = loading; }; - @observable - public text: string = ''; - @action - public setText = (text: string) => { + @observable public text: string = ''; + @action public setText = (text: string) => { this.text = text; }; - @observable - public selectedText: string = ''; - @action - public setSelectedText = (text: string) => { + @observable public selectedText: string = ''; + @action public setSelectedText = (text: string) => { this.selectedText = text; }; - @observable - public dataJson: string = ''; + @observable public dataJson: string = ''; public dataChatPrompt: string | undefined = undefined; - @action - public setDataJson = (text: string) => { + @action public setDataJson = (text: string) => { if (text === '') this.dataChatPrompt = ''; this.dataJson = text; }; - @observable - public imgDesc: string = ''; - @action - public setImgDesc = (text: string) => { + @observable public imgDesc: string = ''; + @action public setImgDesc = (text: string) => { this.imgDesc = text; }; - @observable - public imgUrls: string[][] = []; - @action - public setImgUrls = (imgs: string[][]) => { + @observable public imgUrls: string[][] = []; + @action public setImgUrls = (imgs: string[][]) => { this.imgUrls = imgs; }; - @observable - public mode: GPTPopupMode = GPTPopupMode.SUMMARY; - @action - public setMode = (mode: GPTPopupMode) => { + @observable public mode: GPTPopupMode = GPTPopupMode.SUMMARY; + @action public setMode = (mode: GPTPopupMode) => { this.mode = mode; }; - @observable - public highlightRange: number[] = []; + @observable public highlightRange: number[] = []; @action callSummaryApi = () => {}; - @observable - private done: boolean = false; - @action - public setDone = (done: boolean) => { + @observable private done: boolean = false; + @action public setDone = (done: boolean) => { this.done = done; this.chatMode = false; }; - @observable - private sortDone: boolean = false; // this is so redundant but the og done variable was causing weird unknown problems and im just a girl - - @action - public setSortDone = (done: boolean) => { - this.sortDone = done; - }; - // change what can be a ref into a ref - @observable - private sidebarId: string = ''; - @action - public setSidebarId = (id: string) => { + @observable private sidebarId: string = ''; + @action public setSidebarId = (id: string) => { this.sidebarId = id; }; - @observable - private imgTargetDoc: Doc | undefined; - @action - public setImgTargetDoc = (anchor: Doc) => { + @observable private imgTargetDoc: Doc | undefined; + @action public setImgTargetDoc = (anchor: Doc) => { this.imgTargetDoc = anchor; }; - @observable - private textAnchor: Doc | undefined; - @action - public setTextAnchor = (anchor: Doc) => { + @observable private textAnchor: Doc | undefined; + @action public setTextAnchor = (anchor: Doc) => { this.textAnchor = anchor; }; - @observable - public sortDesc: string = ''; - + @observable public sortDesc: string = ''; @action public setSortDesc = (t: string) => { this.sortDesc = t; }; - @observable onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void; - @observable onQuizRandom?: () => void; + onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void; + onQuizRandom?: () => void; @observable cardsDoneLoading = false; + @observable collectionDoc: Doc | undefined = undefined; + @action setCollectionDoc(doc: Doc | undefined) { + this.collectionDoc = doc; + } + @action setCardsDoneLoading(done: boolean) { - console.log(done + 'HI HIHI'); this.cardsDoneLoading = done; } @@ -186,39 +153,27 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { */ generateQuiz = async () => { this.setLoading(true); - this.setSortDone(false); - const quizType = this.quizMode; + await this.regenerateCallback?.(); const selected = DocumentView.SelectedDocs().lastElement(); - - const questionText = 'Question: ' + StrCast(selected['gptInputText']); - - if (StrCast(selected['gptRubric']) === '') { - const rubricText = 'Rubric: ' + (await this.generateRubric(StrCast(selected['gptInputText']), selected)); + if (!StrCast(selected.gptRubric)) { + await this.generateRubric(StrCast(selected.gptInputText), selected); } - const rubricText = 'Rubric: ' + StrCast(selected['gptRubric']); - const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText; - try { - const res = await gptAPICall(queryText, GPTCallType.QUIZ); - if (!res) { - console.error('GPT call failed'); - return; - } - console.log(res); - this.setQuizResp(res); - this.conversationArray.push(res); + const res = await gptAPICall('Question: ' + StrCast(selected.gptInputText) + ' UserAnswer: ' + this.quizAnswer + '. Rubric: ' + StrCast(selected.gptRubric), GPTCallType.QUIZ); + if (res) { + this.setQuizResp(res); + this.conversationArray.push(res); - this.setLoading(false); - this.setSortDone(true); + this.setLoading(false); + this.onQuizRandom?.(); + } else { + console.error('GPT provided no response'); + } } catch (err) { - console.error('GPT call failed'); - } - - if (this.onQuizRandom) { - this.onQuizRandom(); + console.error('GPT call failed', err); } }; @@ -231,10 +186,10 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { generateRubric = async (inputText: string, doc: Doc) => { try { const res = await gptAPICall(inputText, GPTCallType.RUBRIC); - doc['gptRubric'] = res; + doc.gptRubric = res; return res; } catch (err) { - console.error('GPT call failed'); + console.error('GPT call failed', err); } }; @@ -244,7 +199,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { * Callback function that causes the card view to update the childpair string list * @param callback */ - @action public setRegenerateCallback(callback: () => Promise<void>) { + @action public setRegenerateCallback(collectionDoc: Doc | undefined, callback: null | (() => Promise<void>)) { + this.setCollectionDoc(collectionDoc); this.regenerateCallback = callback; } @@ -262,37 +218,21 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { * Generates a response to the user's question depending on the type of their question */ generateCard = async () => { - console.log(this.chatSortPrompt + 'USER PROMPT'); this.setLoading(true); - this.setSortDone(false); - if (this.regenerateCallback) { - await this.regenerateCallback(); - } + await this.regenerateCallback?.(); try { - // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE); - const questionNumber = questionType.split(' ')[0]; - console.log(questionType); - let res = ''; - - switch (questionNumber) { - case '1': - case '2': - case '4': - res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); - break; - case '6': - res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); - break; - default: - const selected = DocumentView.SelectedDocs().lastElement(); - const questionText = StrCast(selected!['gptInputText']); - - res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt); - break; - } + const questionNumber = questionType.split(' ')[0][0]; + const res = await (() => { + switch (questionNumber) { + case '1': + case '2': + case '4': return gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt); + case '6': return gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt); + default: return gptAPICall(StrCast(DocumentView.SelectedDocs().lastElement()?.gptInputText), GPTCallType.INFO, this.chatSortPrompt); + }})(); // prettier-ignore // Trigger the callback with the result if (this.onSortComplete) { @@ -308,7 +248,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { // Set the extracted explanation to sortRespText this.setSortRespText(explanation); - this.conversationArray.push(this.sortRespText); + runInAction(() => this.conversationArray.push(this.sortRespText)); this.scrollToBottom(); console.log(res); @@ -318,7 +258,6 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } this.setLoading(false); - this.setSortDone(true); }; /** @@ -448,7 +387,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { private getPreviewUrl = (source: string) => source.split('.').join('_m.'); - constructor(props: GPTPopupProps) { + constructor(props: object) { super(props); makeObservable(this); GPTPopup.Instance = this; @@ -498,9 +437,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { onClick={() => { this.conversationArray = ['Define the selected card!']; this.setMode(GPTPopupMode.QUIZ); - if (this.onQuizRandom) { - this.onQuizRandom(); - } + this.onQuizRandom?.(); }} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} @@ -515,18 +452,25 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { </div> ); - handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => { + @action + handleKeyPress = (e: React.KeyboardEvent, isSort: boolean) => { if (e.key === 'Enter') { e.stopPropagation(); if (isSort) { this.conversationArray.push(this.chatSortPrompt); - await this.generateCard(); - this.chatSortPrompt = ''; + this.generateCard().then( + action(() => { + this.chatSortPrompt = ''; + }) + ); } else { this.conversationArray.push(this.quizAnswer); - await this.generateQuiz(); - this.quizAnswer = ''; + this.generateQuiz().then( + action(() => { + this.quizAnswer = ''; + }) + ); } this.scrollToBottom(); @@ -569,21 +513,22 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { }; sortBox = () => ( - <div style={{ height: '80%' }}> + <div className="gptPopup-sortBox" style={{ height: '80%' }}> {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')} <> - {!this.cardsDoneLoading ? ( - <div className="content-wrapper"> - <div className="loading-spinner"> - <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> - {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>} + { + !this.cardsDoneLoading ? ( + <div className="content-wrapper"> + <div className="loading-spinner"> + <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> + {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>} + </div> </div> - </div> - ) : this.mode === GPTPopupMode.CARD ? ( - this.cardMenu() - ) : ( - this.cardActual(this.mode) - ) // Call the functions to render JSX + ) : this.mode === GPTPopupMode.CARD ? ( + this.cardMenu() + ) : ( + this.cardActual(this.mode) + ) // Call the functions to render JSX } </> </div> @@ -741,6 +686,15 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { {(this.mode === GPTPopupMode.SORT || this.mode === GPTPopupMode.QUIZ) && ( <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => (this.mode = GPTPopupMode.CARD)} style={{ right: '50px', position: 'absolute' }} /> )} + <Toggle + tooltip="Clear Chat filter" + toggleType={ToggleType.BUTTON} + type={Type.PRIM} + toggleStatus={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat')} + text={Doc.hasDocFilter(this.collectionDoc, 'tags', '#chat') ? 'filtered' : ''} + color="red" + onClick={() => this.collectionDoc && Doc.setDocFilter(this.collectionDoc, 'tags', '#chat', 'remove')} + /> <IconButton color={StrCast(SettingsManager.userVariantColor)} tooltip="close" @@ -777,7 +731,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> { } return ( - <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}> + <div className="summary-box" style={{ display: this.Visible ? 'flex' : 'none' }}> {content} <div className="resize-handle" /> </div> diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a225c4b59..030251762 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -7,6 +7,10 @@ left: 0; } +:root { + --devicePixelRatio: 1; // the actual value of this will be set in PDFViewer.tsx; +} + .pdfViewerDash, .pdfViewerDash-interactive { position: absolute; @@ -19,9 +23,16 @@ overflow-x: hidden; transform-origin: top left; + .annotationLayer { + transform: scale(var(--devicePixelRatio)); + } .textLayer { opacity: unset; mix-blend-mode: multiply; // bcz: makes text fuzzy! + transform: scale(var(--devicePixelRatio)); + } + [data-main-rotation='90'] { + transform: scale(var(--devicePixelRatio)) rotate(90deg) translateY(-100%); } .textLayer ::selection { background: #accef76a; @@ -39,6 +50,7 @@ .page { position: relative; border: unset; + height: 100% !important; } .pdfViewerDash-text-selected { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 358557ad7..8728ce99c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -11,7 +11,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, numberRange } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; @@ -30,6 +30,7 @@ import { GPTPopup } from './GPTPopup/GPTPopup'; import './PDFViewer.scss'; import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; import ReactLoading from 'react-loading'; +import { Transform } from '../../util/Transform'; interface IViewerProps extends FieldViewProps { pdfBox: PDFBox; @@ -40,7 +41,7 @@ interface IViewerProps extends FieldViewProps { pdf: Pdfjs.PDFDocumentProxy; url: string; sidebarAddDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean; - loaded?: (nw: number, nh: number, np: number) => void; + loaded: (p: { width: number; height: number }, pages: number) => void; // eslint-disable-next-line no-use-before-define setPdfViewer: (view: PDFViewer) => void; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); @@ -146,32 +147,30 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } }; - @observable _scrollHeight = 0; + @computed get _scrollHeight() { + return this._pageSizes.reduce((size, page) => size + page.height, 0); + } - @action - initialLoad = async () => { + initialLoad = () => { + const page0or180 = (page: { rotate: number }) => page.rotate === 0 || page.rotate === 180; if (this._pageSizes.length === 0) { - this._pageSizes = Array<{ width: number; height: number }>(this._props.pdf.numPages); - await Promise.all( - this._pageSizes.map((val, i) => - this._props.pdf.getPage(i + 1).then( - action((page: Pdfjs.PDFPageProxy) => { - const page0or180 = page.rotate === 0 || page.rotate === 180; - this._pageSizes.splice(i, 1, { - width: page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], - height: page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], - }); - if (i === this._props.pdf.numPages - 1) { - this._props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this._props.pdf.numPages); - } - }) - ) + const devicePixelRatio = window.devicePixelRatio; + document.documentElement?.style.setProperty('--devicePixelRatio', window.devicePixelRatio.toString()); // set so that css can use this to adjust various PDFJs divs + Promise.all( + numberRange(this._props.pdf.numPages).map(i => + this._props.pdf.getPage(i + 1).then(page => ({ + width: (page.view[page0or180(page) ? 2 : 3] - page.view[page0or180(page) ? 0 : 1]) * devicePixelRatio, + height: (page.view[page0or180(page) ? 3 : 2] - page.view[page0or180(page) ? 1 : 0]) * devicePixelRatio, + })) ) + ).then( + action(pages => { + this._pageSizes = pages; + this._props.loaded(pages.lastElement(), this._props.pdf.numPages); + this.createPdfViewer(); + }) ); } - runInAction(() => { - this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72; - }); }; _scrollStopper: undefined | (() => void); @@ -197,14 +196,12 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { crop = (region: Doc | undefined, addCrop?: boolean) => this._props.crop(region, addCrop); @action - setupPdfJsViewer = async () => { + setupPdfJsViewer = () => { if (this._viewerIsSetup) return; this._viewerIsSetup = true; this._showWaiting = true; this._props.setPdfViewer(this); - await this.initialLoad(); - - this.createPdfViewer(); + this.initialLoad(); }; pagesinit = () => { @@ -377,7 +374,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) { this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document); } - if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) { this._props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this.isAnnotating = true; @@ -533,7 +530,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } getScrollHeight = () => this._scrollHeight; - scrollXf = () => this._props.ScreenToLocalTransform().translate(0, this._mainCont.current ? NumCast(this._props.layoutDoc._layout_scrollTop) : 0); + scrollXf = () => this._props.ScreenToLocalTransform().translate(0, this._mainCont.current ? NumCast(this._props.layoutDoc._layout_scrollTop) / 1.333 : 0); overlayTransform = () => this.scrollXf().scale(1 / NumCast(this._props.layoutDoc._freeform_scale, 1)); panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1); panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); @@ -554,7 +551,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { className="pdfViewerDash-overlay" style={{ mixBlendMode, - display: display, + display, + transform: `scale(${Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS})`, pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> <CollectionFreeFormView @@ -600,6 +598,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } savedAnnotations = () => this._savedAnnotations; addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); + screenToMarqueeXf = () => this.props.pdfBox.DocumentView?.()?.screenToContentsTransform().scale(Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS) ?? Transform.Identity(); render() { TraceMobx(); return ( @@ -619,17 +618,18 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { {this.annotationLayer} {this.overlayLayer} {this._showWaiting ? <img alt="" className="pdfViewerDash-waiting" src="/assets/loading.gif" /> : null} - {!this._mainCont.current || !this._annotationLayer.current ? null : ( + {!this._mainCont.current || !this._annotationLayer.current || !this.props.pdfBox.DocumentView ? null : ( <MarqueeAnnotator ref={this._marqueeref} Document={this._props.Document} getPageFromScroll={this.getPageFromScroll} anchorMenuClick={this._props.anchorMenuClick} scrollTop={0} - isNativeScaled + annotationLayerScaling={() => Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS} annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)} addDocument={this.addDocumentWrapper} - docView={this._props.pdfBox.DocumentView!} + docView={this.props.pdfBox.DocumentView} + screenTransform={this.screenToMarqueeXf} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} |
