From b60b1863be5ecf987f3d3f2483e28e74f634853f Mon Sep 17 00:00:00 2001 From: bobzel 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') 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() { {...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 { - 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>/g, '$2

'); // prettier-ignore + this._inDrop = true; + view.pasteHTML(html.split(' 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 { 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 => { - 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