From 78ac87b8acf63079071e5e8805692ed8c30042ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 23 Apr 2025 22:02:36 -0400
Subject: lots of typechecking fixes.

---
 .../collections/collectionFreeForm/FaceCollectionBox.tsx    | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 72485aa86..ad05a798b 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -106,7 +106,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
                     // assign the face in the image that's closest to the face collection's face
                     if (faceAnno) {
-                        faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face));
+                        DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face)!);
                         FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
                         faceAnno.$face = this.Document[DocData];
                     }
@@ -116,7 +116,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
             ?.filter(doc => DocCast(doc.face)?.type === DocumentType.UFACE)
             .forEach(faceAnno => {
                 const imgDoc = faceAnno;
-                faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face));
+                DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face)!);
                 FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
                 faceAnno.$face = this.Document[DocData];
             });
@@ -187,7 +187,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                         })}>
                         {FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
-                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
+                            const [name, type] = ImageCastToNameType(doc?.[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
                             return (
                                 <div
                                     className="image-wrapper"
@@ -197,7 +197,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             this,
                                             e,
                                             () => {
-                                                const dragDoc = DocListCast(doc.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
+                                                const dragDoc = DocListCast(doc?.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
                                                 DragManager.StartDocumentDrag([e.target as HTMLElement], new DragManager.DocumentDragData([dragDoc], dropActionType.embed), e.clientX, e.clientY);
                                                 return true;
                                             },
@@ -205,7 +205,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             emptyFunction
                                         )
                                     }>
-                                    <img onClick={() => DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
+                                    <img onClick={() => doc && DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
                                     <div className="remove-item">
                                         <IconButton tooltip={'Remove Doc From Face Collection'} onPointerDown={() => this.removeFaceImageFromUniqueFace(doc)} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
                                     </div>
@@ -239,11 +239,12 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     addDocument = (doc: Doc | Doc[], annotationKey?: string) => {
         const uniqueFaceDoc = doc instanceof Doc ? doc : doc[0];
         const added = uniqueFaceDoc.type === DocumentType.UFACE;
         if (added) {
-            Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
+            Doc.MyFaceCollection && Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
             Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
         }
         return added;
-- 
cgit v1.2.3-70-g09d2


From b60b1863be5ecf987f3d3f2483e28e74f634853f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 25 Apr 2025 16:02:18 -0400
Subject: fixed face collection to show up.  fixed pasting text dirextly into
 freeformview to create docs properly. also fixed paste to match source text
 styling better.  fixed copying out of pdf.  fixed formatting of gpt (())
 calls from text box by using paste handling code.

---
 .../collectionFreeForm/FaceCollectionBox.tsx       |  3 ++
 .../views/nodes/formattedText/FormattedTextBox.tsx | 62 ++++++++++++++++++++--
 .../views/nodes/formattedText/RichTextRules.ts     |  3 +-
 src/client/views/pdf/PDFViewer.tsx                 |  1 +
 src/client/views/search/FaceRecognitionHandler.tsx |  2 +-
 5 files changed, 65 insertions(+), 6 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index ad05a798b..624c85beb 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -24,6 +24,7 @@ import { FaceRecognitionHandler } from '../../search/FaceRecognitionHandler';
 import { CollectionStackingView } from '../CollectionStackingView';
 import './FaceCollectionBox.scss';
 import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import { returnEmptyDocViewList } from '../../StyleProvider';
 
 /**
  * This code is used to render the sidebar collection of unique recognized faces, where each
@@ -268,6 +269,8 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     {...this._props} //
                     styleProvider={this.stackingStyleProvider}
                     Document={Doc.ActiveDashboard}
+                    DocumentView={undefined}
+                    docViewPath={returnEmptyDocViewList}
                     fieldKey="myUniqueFaces"
                     moveDocument={this.moveDocument}
                     addDocument={this.addDocument}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9897a0062..164c64107 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1344,8 +1344,63 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return text;
     };
 
-    handlePaste = (view: EditorView, event: Event /* , slice: Slice */): boolean => {
-        const pdfAnchorId = (event as ClipboardEvent).clipboardData?.getData('dash/pdfAnchor');
+    handlePaste = (view: EditorView, event: ClipboardEvent /* , slice: Slice */): boolean => {
+        return this.doPaste(view, event.clipboardData);
+    };
+    doPaste = (view: EditorView, data: DataTransfer | null) => {
+        const html = data?.getData('text/html');
+        const text = data?.getData('text/plain');
+        const pdfAnchorId = data?.getData('dash/pdfAnchor');
+        if (html && !pdfAnchorId) {
+            const replaceDivsWithParagraphs = (expr: string) => {
+                // Create a temporary DOM container
+                const container = document.createElement('div');
+                container.innerHTML = expr;
+
+                // Recursive function to process all divs
+                function processDivs(node: HTMLElement) {
+                    // Get all div elements in the current node (live collection)
+                    const divs = node.getElementsByTagName('div');
+
+                    // We need to convert to array because we'll be modifying the DOM
+                    const divsArray = Array.from(divs);
+
+                    for (const div of divsArray) {
+                        // Create replacement paragraph
+                        const p = document.createElement('p');
+
+                        // Copy all attributes
+                        for (const attr of div.attributes) {
+                            p.setAttribute(attr.name, attr.value);
+                        }
+
+                        // Move all child nodes
+                        while (div.firstChild) {
+                            p.appendChild(div.firstChild);
+                        }
+
+                        // Replace the div with the paragraph
+                        div.parentNode?.replaceChild(p, div);
+
+                        // Process any nested divs that were moved into the new paragraph
+                        processDivs(p);
+                    }
+                }
+
+                // Start processing from the container
+                processDivs(container);
+
+                return container.innerHTML;
+            };
+            const fixedHTML = replaceDivsWithParagraphs(html);
+            // .replace(/<div\b([^>]*)>(.*?)<\/div>/g, '<p$1>$2</p>'); // prettier-ignore
+            this._inDrop = true;
+            view.pasteHTML(html.split('<p').length < 2 ? fixedHTML : html);
+            this._inDrop = false;
+
+            return true;
+        }
+
         return !!(pdfAnchorId && this.addPdfReference(pdfAnchorId));
     };
 
@@ -1485,9 +1540,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
         if (this._props.isContentActive()) this.prepareForTyping();
         if (this.EditorView && FormattedTextBox.PasteOnLoad) {
-            const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
+            this.doPaste(this.EditorView, FormattedTextBox.PasteOnLoad.clipboardData);
             FormattedTextBox.PasteOnLoad = undefined;
-            pdfAnchorId && this.addPdfReference(pdfAnchorId);
         }
         if (this._props.autoFocus) setTimeout(() => this.EditorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
     }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 26ccf6931..f26a75fe4 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -349,7 +349,8 @@ export class RichTextRules {
                 let count = 0; // ignore first return value which will be the notation that chat is pending a result
                 Doc.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => {
                     if (count) {
-                        const tr = this.TextBox.EditorView?.state.tr.insertText(' ' + (gptval as string));
+                        this.TextBox.EditorView?.pasteText(' ' + (gptval as string), undefined);
+                        const tr = this.TextBox.EditorView?.state.tr; //.insertText(' ' + (gptval as string));
                         tr && this.TextBox.EditorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(end + 2), tr.doc.resolve(end + 2 + (gptval as string).length))));
                         RichTextMenu.Instance?.elideSelection(this.TextBox.EditorView?.state, true);
                     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 041fc0de7..fc2567fbc 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -146,6 +146,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
                 e.clipboardData.setData('dash/pdfAnchor', anchor[DocData][Id]);
             }
             e.preventDefault();
+            e.stopPropagation();
         }
     };
 
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index cb837e3ab..3ad5bc844 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -43,7 +43,7 @@ export class FaceRecognitionHandler {
      * Loads an image
      */
     private static loadImage = (imgUrl: ImageField): Promise<HTMLImageElement> => {
-        const [name, type] = ImageCastToNameType(imgUrl.url.href);
+        const [name, type] = ImageCastToNameType(imgUrl);
         const imageURL = `${name}_o.${type}`;
 
         return new Promise((resolve, reject) => {
-- 
cgit v1.2.3-70-g09d2

