diff options
Diffstat (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 118 |
1 files changed, 46 insertions, 72 deletions
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9d3050d90..5f132ecdf 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -14,11 +14,11 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti import { EditorView, NodeViewConstructor } from 'prosemirror-view'; import * as React from 'react'; import { BsMarkdownFill } from 'react-icons/bs'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, imageUrlToBase64, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; -import { Id } from '../../../../fields/FieldSymbols'; +import { Id, ToString } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { PrefetchProxy } from '../../../../fields/Proxy'; @@ -27,7 +27,7 @@ import { ComputedField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { emptyFunction, numberRange, unimplementedFunction, Utils } from '../../../../Utils'; -import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT'; +import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -78,14 +78,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } - public static MakeConfig(rules?: RichTextRules, props?: FormattedTextBoxProps) { + public static MakeConfig(rules?: RichTextRules, textBox?: FormattedTextBox) { return { schema, plugins: [ inputRules(rules?.inpRules ?? { rules: [] }), - ...(props ? [FormattedTextBox.richTextMenuPlugin(props)] : []), + ...(textBox?._props ? [FormattedTextBox.richTextMenuPlugin(textBox._props)] : []), history(), - keymap(buildKeymap(schema, props ?? {})), + keymap(buildKeymap(schema, textBox)), keymap(baseKeymap), new Plugin({ props: { attributes: { class: 'ProseMirror-example-setup-style' } } }), new Plugin({ view: () => new FormattedTextBoxComment() }), @@ -153,7 +153,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } // eslint-disable-next-line no-return-assign - @computed get config() { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this._props); } // prettier-ignore + @computed get config() { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this); } // prettier-ignore @computed get _recordingDictation() { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore @computed get SidebarShown() { return !!(this._showSidebar || this.layoutDoc._layout_showSidebar); } // prettier-ignore @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore @@ -226,22 +226,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB return anchor; }; - gptPDFFlashcards = async () => { - const queryText = window.getSelection()?.toString() ?? ''; - try { - if (queryText) { - const res = await gptAPICall(queryText, GPTCallType.FLASHCARD); - AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)); - } - } catch (err) { - console.error(err); - } - }; - @action setupAnchorMenu = () => { AnchorMenu.Instance.Status = 'marquee'; - AnchorMenu.Instance.gptFlashcards = this.gptPDFFlashcards; + // AnchorMenu.Instance.gptFlashcards = this.selectionToFlashcards; + AnchorMenu.Instance.makeLabels = unimplementedFunction; AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument; AnchorMenu.Instance.OnClick = () => { !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); @@ -256,9 +245,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB if (target) { anchor.followLinkAudio = true; let stopFunc: () => void = emptyFunction; - const targetData = target[DocData]; - targetData.mediaState = mediaState.Recording; - DictationManager.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore + target.$mediaState = mediaState.Recording; + DictationManager.recordAudioAnnotation(target[DocData], Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore const reactionDisposer = reaction( () => target.mediaState, @@ -327,6 +315,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB (node.attrs.hideValue ? '' : Field.toJavascriptString(refDoc[fieldKey] as FieldType)) ); } + if (node.type === this.EditorView?.state.schema.nodes.dashDoc) { + const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc); + return refDoc[ToString](); + } return ''; }; dispatchTransaction = (tx: Transaction) => { @@ -1008,31 +1000,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' }); }; - findImageTags = async () => { - const c = this.ProseRef?.getElementsByTagName('img'); - if (c) { - for (const i of c) { - // console.log(canvas.toDataURL()); - // canvas.style.zIndex = '2000000'; - // document.body.appendChild(canvas); - if (i.className !== 'ProseMirror-separator') this.getImageDesc(i.src); - } - } - }; - - getImageDesc = async (u: string) => { - try { - const hrefBase64 = await imageUrlToBase64(u); - const response = await gptImageLabel( - hrefBase64, - 'Make flashcards out of this text and image with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' + (this.dataDoc.text as RichTextField)?.Text - ); - AnchorMenu.Instance.transferToFlashcard(response || 'Something went wrong', NumCast(this.dataDoc['x']), NumCast(this.dataDoc['y'])); - } catch (error) { - console.log('Error', error); - } - }; - animateRes = (resIndex: number, newText: string) => { if (resIndex < newText.length) { const marks = this.EditorView?.state.storedMarks ?? []; @@ -1100,7 +1067,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB }; const link = CreateLinkToActiveAudio(textanchorFunc, false).lastElement(); if (link) { - link[DocData].isDictation = true; + link.$isDictation = true; const audioanchor = Cast(link.link_anchor_2, Doc, null); const textanchor = Cast(link.link_anchor_1, Doc, null); if (audioanchor) { @@ -1110,7 +1077,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB audioId: audioanchor[Id], textId: textanchor[Id], }); - textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode; + textanchor.$title = 'dictation:' + audiotag.attrs.timeCode; const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag); const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size)); this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size))); @@ -1491,6 +1458,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } _didScroll = false; _scrollStopper: undefined | (() => void); + scrollToSelection = () => { + if (this.EditorView && this._ref.current) { + const editorView = this.EditorView; + const docPos = editorView.coordsAtPos(editorView.state.selection.to); + const viewRect = this._ref.current.getBoundingClientRect(); + const scrollRef = this._scrollRef; + const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined; + const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined; + if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) { + const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE); + const scrollPos = scrollRef.scrollTop + shift * this.ScreenToLocalBoxXf().Scale; + if (this._focusSpeed !== undefined) { + setTimeout(() => { + scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed || 0, scrollRef, scrollPos, 'ease', this._scrollStopper)); + }); + } else { + scrollRef.scrollTo({ top: scrollPos }); + } + this._didScroll = true; + } + } + return true; + }; // eslint-disable-next-line @typescript-eslint/no-explicit-any setupEditor(config: any, fieldKey: string) { const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]); @@ -1499,31 +1489,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB this.EditorView?.destroy(); this._editorView = new EditorView(this.ProseRef, { state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config), - handleScrollToSelection: editorView => { - const docPos = editorView.coordsAtPos(editorView.state.selection.to); - const viewRect = this._ref.current!.getBoundingClientRect(); - const scrollRef = this._scrollRef; - const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined; - const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined; - if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) { - const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE); - const scrollPos = scrollRef.scrollTop + shift * this.ScreenToLocalBoxXf().Scale; - if (this._focusSpeed !== undefined) { - setTimeout(() => { - scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed || 0, scrollRef, scrollPos, 'ease', this._scrollStopper)); - }); - } else { - scrollRef.scrollTo({ top: scrollPos }); - } - this._didScroll = true; - } - return true; - }, + handleScrollToSelection: this.scrollToSelection, dispatchTransaction: this.dispatchTransaction, nodeViews: FormattedTextBox._nodeViews(this), clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, }); + // bcz: major hack! a patch to prosemirror broke scrolling to selection when the selection is not a dom selection + // this replaces prosemirror's scrollToSelection function with Dash's + (this.EditorView as unknown as { scrollToSelection: unknown }).scrollToSelection = this.scrollToSelection; const { state } = this._editorView; if (!rtfField) { const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc; @@ -1980,7 +1954,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB <SidebarAnnos ref={this._sidebarRef} {...this._props} - Document={this.Document} + Doc={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} usePanelWidth @@ -2108,7 +2082,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) => - [view._props.PanelWidth() / view.screenToLocalScale(), view._props.PanelHeight() / view.screenToLocalScale()]; // prettier-ignore + [view?._props.PanelWidth() /(view?.screenToLocalScale()??1), view?._props.PanelHeight() / (view?.screenToLocalScale()??1)]; // prettier-ignore const scrMargin = [Math.max(0, (scrSize(2)[0] - scrSize(1)[0]) / 2), Math.max(0, (scrSize(2)[1] - scrSize(1)[1]) / 2)]; const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale); const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale); |