From 66f2b03283a1e42c48b1c16b4344b730c0a2e9f3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Oct 2024 14:19:35 -0400
Subject: cleaned up comparisonBox a bit - fixed text doc placeholder text and
 fixed parsing keyword/question/answer from flashcard creator.  also fixed
 text boxes within in comparisonBox

---
 src/client/views/nodes/DocumentView.tsx | 9 ---------
 1 file changed, 9 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f7aba7542..c052a2823 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -576,15 +576,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
 
-            // creates menu for the user to select how to reveal the flashcards
-            // if (this.Document._layout_isFlashcard) {
-            //     const revealOptions = cm.findByDescription('Reveal Options');
-            //     const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
-            //     revealItems.push({ description: 'Hover', event: () => { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'hover'; }, icon: 'hand-point-up' }); // prettier-ignore
-            //     revealItems.push({ description: 'Flip', event: () => { this.layoutDoc[`_${this._props.fieldKey}_revealOp`] = 'flip'; }, icon: 'rotate' }); // prettier-ignore
-            //     !revealOptions && cm.addItem({ description: 'Reveal Options', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
-            // }
-
             if (this._props.bringToFront) {
                 const zorders = cm.findByDescription('ZOrder...');
                 const zorderItems = zorders?.subitems ?? [];
-- 
cgit v1.2.3-70-g09d2


From bb8fe2933154c6db70cfe5da1e890535bc9012d4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Oct 2024 20:21:33 -0400
Subject: fixed verticalalign of text boxes on load when existing text was
 there.  fixe scrolling of vertical align textboxes when fitwidth is set. 
 added flashcard contextmenu to comparisonbox and

---
 src/client/views/StyleProvider.tsx                 |   3 +-
 src/client/views/nodes/ComparisonBox.tsx           | 217 ++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            |  21 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  18 +-
 4 files changed, 115 insertions(+), 144 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 02e0a34d8..8859f6464 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -54,7 +54,6 @@ export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: nu
 }
 
 export function border(doc: Doc, pw: number, ph: number, rad: number = 0, inset: number = 0) {
-    if (!rad) rad = 0;
     const width = pw * inset;
     const height = ph * inset;
 
@@ -218,7 +217,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             const radiusRatio = borderRadius / docWidth;
             const radius = radiusRatio * ((2 * borderWidth) + docWidth);
 
-            const borderPath = doc && border(doc, NumCast(doc._width), NumCast(doc._height), radius, -ratio/2 ?? 0);
+            const borderPath = doc && border(doc, NumCast(doc._width), NumCast(doc._height), radius, -ratio/2);
                 return !borderPath
                     ? null
                     : {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 672008968..0582bc996 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -15,7 +15,7 @@ import { BoolCast, DocCast, NumCast, RTFCast, StrCast, toList } from '../../../f
 import { nullAudio } from '../../../fields/URLField';
 import { GPTCallType, gptAPICall, gptImageLabel } from '../../apis/gpt/GPT';
 import { DocUtils, FollowLinkScript } from '../../documents/DocUtils';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
@@ -31,6 +31,7 @@ import './ComparisonBox.scss';
 import { DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 const API_URL = 'https://api.unsplash.com/search/photos';
 
@@ -55,17 +56,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ComparisonBox, fieldKey);
     }
+    private SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+
     static qtoken = 'Question: ';
     static ktoken = 'Keyword: ';
     static atoken = 'Answer: ';
-    private SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+    private _slideTiming = 200;
+    private _sideBtnWidth = 35;
     private _closeRef = React.createRef<HTMLDivElement>();
     private _disposers: { [key: string]: DragManager.DragDropDisposer | undefined } = {};
     private _reactDisposer: IReactionDisposer | undefined;
-    constructor(props: FieldViewProps) {
-        super(props);
-        makeObservable(this);
-    }
 
     @observable private _inputValue = '';
     @observable private _outputValue = '';
@@ -77,6 +77,47 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @observable private _renderSide = this.frontKey;
     @observable private _recognition = new this.SpeechRecognition();
 
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
+
+    componentDidMount() {
+        this._props.setContentViewBox?.(this);
+        this._reactDisposer = reaction(
+            () => this._props.isSelected(), // when this reaction should update
+            selected => {
+                if (selected && this.isFlashcard) this.activateContent();
+                !selected && (this._childActive = false);
+            }, // what it should update to
+            { fireImmediately: true }
+        );
+    }
+
+    componentWillUnmount() {
+        this._reactDisposer?.();
+    }
+
+    protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string) => {
+        this._disposers[fieldKey]?.();
+        if (ele) {
+            this._disposers[fieldKey] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc);
+        }
+    };
+
+    private internalDrop = undoable((e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
+        if (dropEvent.complete.docDragData) {
+            const { droppedDocuments } = dropEvent.complete.docDragData;
+            const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocuments, this.Document, (doc: Doc | Doc[]) => this.addDoc(toList(doc).lastElement(), fieldKey));
+            Doc.SetContainer(droppedDocuments.lastElement(), this.dataDoc);
+            !added && e.preventDefault();
+            e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
+            // this.childActive = false;
+            return added;
+        }
+        return undefined;
+    }, 'internal drop');
+
     @computed get isFlashcard()   { return BoolCast(this.Document.layout_isFlashcard); } // prettier-ignore
     @computed get frontKey()      { return this._props.fieldKey + '_front'; } // prettier-ignore
     @computed get backKey()       { return this._props.fieldKey + '_back'; } // prettier-ignore
@@ -112,8 +153,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             </Tooltip>
         );
     }
-
-    _sideBtnWidth = 35;
     /**
      * How much the content of the view is being scaled based on its nesting and its fit-to-width settings
      */
@@ -131,35 +170,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         return SnappingManager.HideDecorations ? null : (
             <div className="comparisonBox-bottomMenu" style={{ transform: `scale(${this.uiBtnScaling})` }}>
                 {this.revealOp === flashcardRevealOp.HOVER || !this._props.isSelected() ? null : this.overlayAlternateIcon}
-                {!this._props.isSelected() ? null : (
-                    <>
-                        {this._renderSide === this.frontKey ? null : (
-                            <Tooltip
-                                title={
-                                    <div className="dash-tooltip">Ask GPT to create an answer for the question on the front</div> // prettier-ignore
-                                }>
-                                <div className="comparisonBox-button" onPointerDown={() => this.askGPT(GPTCallType.CHATCARD)}>
-                                    <FontAwesomeIcon icon="lightbulb" size="xl" />
-                                </div>
-                            </Tooltip>
-                        )}
-                        {DocCast(this.Document.embedContainer)?.type_collection !== CollectionViewType.Freeform || this._renderSide === this.backKey ? null : (
-                            <Tooltip title={<div className="dash-tooltip">Create new flashcard stack based on text</div>}>
-                                <div className="comparisonBox-button" onClick={() => this.askGPT(GPTCallType.STACK).then(this.createFlashcardDeck)}>
-                                    <FontAwesomeIcon icon="layer-group" size="xl" />
-                                </div>
-                            </Tooltip>
-                        )}
-                    </>
+                {!this._props.isSelected() || this._renderSide === this.frontKey ? null : (
+                    <Tooltip title={<div className="dash-tooltip">Ask GPT to create an answer for the question on the front</div>}>
+                        <div className="comparisonBox-button" onPointerDown={() => this.askGPT(GPTCallType.CHATCARD)}>
+                            <FontAwesomeIcon icon="lightbulb" size="xl" />
+                        </div>
+                    </Tooltip>
+                )}
+                {!this._props.isSelected() || this._renderSide === this.backKey || CollectionFreeFormView.from(this.DocumentView?.()) ? null : (
+                    <Tooltip title={<div className="dash-tooltip">Create new flashcard stack based on text</div>}>
+                        <div className="comparisonBox-button" onClick={() => this.askGPT(GPTCallType.STACK).then(this.createFlashcardDeck)}>
+                            <FontAwesomeIcon icon="layer-group" size="xl" />
+                        </div>
+                    </Tooltip>
                 )}
             </div>
         );
     }
 
-    @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-        this._inputValue = e.target.value;
-    };
-
     @action activateContent = () => {
         this._childActive = true;
     };
@@ -182,42 +210,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         return false;
     };
 
-    componentDidMount() {
-        this._props.setContentViewBox?.(this);
-        this._reactDisposer = reaction(
-            () => this._props.isSelected(), // when this reaction should update
-            selected => {
-                if (selected && this.isFlashcard) this.activateContent();
-                !selected && (this._childActive = false);
-            }, // what it should update to
-            { fireImmediately: true }
-        );
-    }
-
-    componentWillUnmount() {
-        this._reactDisposer?.();
-    }
-
-    protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string) => {
-        this._disposers[fieldKey]?.();
-        if (ele) {
-            this._disposers[fieldKey] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc);
-        }
-    };
-
-    private internalDrop = undoable((e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
-        if (dropEvent.complete.docDragData) {
-            const { droppedDocuments } = dropEvent.complete.docDragData;
-            const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocuments, this.Document, (doc: Doc | Doc[]) => this.addDoc(toList(doc).lastElement(), fieldKey));
-            Doc.SetContainer(droppedDocuments.lastElement(), this.dataDoc);
-            !added && e.preventDefault();
-            e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
-            // this.childActive = false;
-            return added;
-        }
-        return undefined;
-    }, 'internal drop');
-
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({
             title: 'CompareAnchor:' + this.Document.title,
@@ -296,17 +288,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 false,
                 undefined,
                 action(() => {
-                    if (this._childActive) return;
-                    this._animating = 'all 200ms';
-                    // on click, animate slider movement to the targetWidth
-                    this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
-
-                    setTimeout(
-                        action(() => {
-                            this._animating = '';
-                        }),
-                        200
-                    );
+                    if (!this._childActive) {
+                        this._animating = `all ${this._slideTiming}ms`; // on click, animate slider movement to the targetWidth
+                        this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this._props.PanelWidth();
+                        setTimeout(  action(() => {this._animating = '';  }), this._slideTiming); // prettier-ignore
+                    }
                 })
             );
         }
@@ -381,17 +367,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * Gets the transcription of an audio recording by sending the
      * recording to backend.
      */
-    pushInfo = async () => {
-        const audio = {
-            file: DocCast(this.Document.audio)[DocData].url,
-        };
-        const response = await axios.post('http://localhost:105/recognize/', audio, {
-            headers: {
-                'Content-Type': 'application/json',
-            },
-        });
-        this.Document.phoneticTranscription = response.data.transcription;
-    };
+    pushInfo = () =>
+        axios
+            .post(
+                'http://localhost:105/recognize/', //
+                { file: DocCast(this.Document.audio)[DocData].url },
+                { headers: { 'Content-Type': 'application/json' } }
+            )
+            .then(response => {
+                this.Document.phoneticTranscription = response.data.transcription;
+            });
 
     /**
      * Extracts the id of the youtube video url.
@@ -408,17 +393,14 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * Gets the transcript of a youtube video by sending the video url to the backend.
      * @returns transcription of youtube recording
      */
-    youtubeUpload = async () => {
-        const audio = {
-            file: this.getYouTubeVideoId(StrCast(RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text)),
-        };
-        const response = await axios.post('http://localhost:105/youtube/', audio, {
-            headers: {
-                'Content-Type': 'application/json',
-            },
-        });
-        return response.data.transcription;
-    };
+    youtubeUpload = async () =>
+        axios
+            .post(
+                'http://localhost:105/youtube/', //
+                { file: this.getYouTubeVideoId(StrCast(RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text)) },
+                { headers: { 'Content-Type': 'application/json' } }
+            )
+            .then(response => response.data.transcription);
 
     /**
      * Creates a flashcard (or fills in flashcard data to a specified Doc) from a control string containing a question and answer
@@ -478,15 +460,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * Calls GPT for each flashcard type.
      */
     askGPT = async (callType: GPTCallType) => {
-        const questionText = 'Question: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text);
-        const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.backKey]).text)?.Text);
-        const queryText = questionText + ' UserAnswer: ' + this._inputValue + '. ' + rubricText;
+        const frontText = RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text;
+        const backText = RTFCast(DocCast(this.dataDoc[this.backKey]).text)?.Text;
+        const questionText = 'Question: ' + frontText;
+        const queryText = questionText + (callType == GPTCallType.QUIZ ? ' UserAnswer: ' + this._inputValue + '. ' + ' Rubric: ' + backText : '');
         this.loading = true;
         let res = '';
 
-        if (callType !== GPTCallType.CHATCARD || StrCast(RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text) !== '') {
+        if (callType !== GPTCallType.CHATCARD || frontText) {
             try {
-                res = await gptAPICall(callType == GPTCallType.QUIZ ? queryText : questionText, callType);
+                res = await gptAPICall(queryText, callType);
                 if (!res) {
                     console.error('GPT call failed');
                 } else
@@ -624,6 +607,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         if (this.revealOp === flashcardRevealOp.HOVER) this._renderSide = side;
     };
 
+    flashcardContextMenu = () => {
+        const appearance = ContextMenu.Instance.findByDescription('Appearance...');
+        const appearanceItems = appearance?.subitems ?? [];
+        if (this.Document._layout_isFlashcard) {
+            appearanceItems.push({ description: 'Create ChatCard', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'id-card' });
+        }
+        !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
+    };
+
     testForTextFields = (whichSlot: string) => {
         const slotData = Doc.Get(this.dataDoc, whichSlot, true);
         const slotHasText = slotData instanceof RichTextField || typeof slotData === 'string';
@@ -752,17 +744,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                         <div className="input-box">
                             <textarea
                                 value={this._renderSide === this.backKey ? this._outputValue : this._inputValue}
-                                onChange={this.handleInputChange}
-                                onScroll={e => {
-                                    e.stopPropagation();
-                                    e.preventDefault();
-                                }}
+                                onChange={action(e => {
+                                    this._inputValue = e.target.value;
+                                })}
                                 placeholder={!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? 'Enter a response for GPT to evaluate.' : ''}
                                 readOnly={this._renderSide === this.backKey}
                             />
                             {!this.loading ? null : (
                                 <div className="loading-spinner">
-                                    <ReactLoading type="spin" height={30} width={30} color={'blue'} />
+                                    <ReactLoading type="spin" height={30} width={30} color='blue' />
                                 </div>
                             )}
                         </div>
@@ -793,12 +783,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             return (
                 <div
                     className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */
+                    onContextMenu={this.flashcardContextMenu}
                     onMouseEnter={() => this.hoverFlip(this.backKey)}
                     onMouseLeave={() => this.hoverFlip(this.frontKey)}>
                     {displayBox(this._renderSide, this._props.PanelWidth() - 3)}
                     {this.loading ? (
                         <div className="loading-spinner">
-                            <ReactLoading type="spin" height={30} width={30} color={'blue'} />
+                            <ReactLoading type="spin" height={30} width={30} color="blue" />
                         </div>
                     ) : null}
                     {this.flashcardMenu}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c052a2823..a343b9a39 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -16,12 +16,11 @@ import { List } from '../../../fields/List';
 import { PrefetchProxy } from '../../../fields/Proxy';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, ImageCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { AudioAnnoState } from '../../../server/SharedMediaTypes';
 import { DocServer } from '../../DocServer';
-import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
 import { DocUtils, FollowLinkScript } from '../../documents/DocUtils';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
@@ -492,21 +491,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
         input.click();
     };
 
-    askGPT = async (): Promise<string | undefined> => {
-        const queryText = RTFCast(DocCast(this.dataDoc[this.props.fieldKey + '_1']).text)?.Text;
-        try {
-            const res = await gptAPICall('Question: ' + StrCast(queryText), GPTCallType.CHATCARD);
-            if (!res) {
-                console.error('GPT call failed');
-                return;
-            }
-            DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
-            console.log(res);
-        } catch (err) {
-            console.error('GPT call failed', err);
-        }
-    };
-
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
         if (this._props.dontSelect?.()) return;
         if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
@@ -569,9 +553,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                 appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => DocumentView.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
             }
             appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), icon: 'map-pin' });
-            if (this.Document._layout_isFlashcard) {
-                appearanceItems.push({ description: 'Create ChatCard', event: () => this.askGPT(), icon: 'id-card' });
-            }
 
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 36f06aaf2..29be8d285 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1515,20 +1515,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 clipboardTextSerializer: this.clipboardTextSerializer,
                 handlePaste: this.handlePaste,
             });
-            const { state, dispatch } = this._editorView;
+            const { state } = this._editorView;
             if (!rtfField) {
                 const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
                 const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
-                if (startupText) {
-                    dispatch(state.tr.insertText(startupText));
-                }
-                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign, 'left'));
-                if (textAlign && textAlign !== 'left') {
+                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign)) || 'left';
+                if (textAlign !== 'left') {
                     selectAll(this._editorView.state, tr => {
-                        this._editorView!.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
+                        this._editorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
                     });
-                    this.tryUpdateDoc(true);
                 }
+                if (startupText) {
+                    this._editorView?.dispatch(this._editorView.state.tr.insertText(startupText));
+                }
+                this.tryUpdateDoc(true);
             }
             this._editorView.TextView = this;
         }
@@ -2147,7 +2147,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                         onScroll={this.onScroll}
                         onDrop={this.ondrop}>
                         <div
-                            className={`formattedTextBox-inner${rounded} ${this.layoutDoc._layout_centered && this.scrollHeight < NumCast(this.layoutDoc._height) ? 'centered' : ''} ${this.layoutDoc.hCentering}`}
+                            className={`formattedTextBox-inner${rounded} ${this.layoutDoc._layout_centered && this.scrollHeight <= (this._props.fitWidth?.(this.Document) ? this._props.PanelHeight() : NumCast(this.layoutDoc._height)) ? 'centered' : ''} ${this.layoutDoc.hCentering}`}
                             ref={this.createDropTarget}
                             style={{
                                 padding: StrCast(this.layoutDoc._textBoxPadding),
-- 
cgit v1.2.3-70-g09d2

