From 1e60b99195bc6748bbad53d173a8a2525216d683 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Mar 2022 08:29:55 -0400
Subject: fixed vertical resizing of web and pdf to not jump after sidebar
 panel is resized

---
 src/client/views/nodes/WebBox.tsx | 2 ++
 1 file changed, 2 insertions(+)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 7ff47107e..7f314bddc 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -590,10 +590,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._draggingSidebar = true;
             const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
             const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+            const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]);
             const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
             const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
             if (ratio >= 1) {
                 this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
                 onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
                 this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
             }
-- 
cgit v1.2.3-70-g09d2


From 70cd320fb02993b2abb17dcfb2a3fd1dfefabb36 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 23 Mar 2022 18:30:59 -0400
Subject: adjsuted pdfBox and WebBox to resize better without glitches when
 using the annotation sidebar.  changed so that when fitWidth is set anything
 can be resized vertically - causes artifacts for videoBoxes.

---
 src/client/views/DocumentDecorations.tsx | 2 +-
 src/client/views/nodes/PDFBox.tsx        | 8 +++-----
 src/client/views/nodes/WebBox.tsx        | 7 +++----
 src/fields/Doc.ts                        | 7 ++++++-
 4 files changed, 13 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d0b5bfe46..39591e417 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -339,7 +339,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 }
                 let actualdW = Math.max(width + (dW * scale), 20);
                 let actualdH = Math.max(height + (dH * scale), 20);
-                const fixedAspect = (nwidth && nheight);
+                const fixedAspect = (nwidth && nheight && !doc._fitWidth);
                 if (fixedAspect) {
                     if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
                         if (dragRight && modifyNativeDim) {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fa23dfbe8..a2e7d2aa3 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -141,12 +141,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setupMoveUpEvents(this, e, (e, down, delta) => {
             const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
             const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]);
             const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
             const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
             if (ratio >= 1) {
                 this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
                 onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
                 this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
             }
@@ -227,8 +225,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </div>;
     }
     sidebarWidth = () => !this.SidebarShown ? 0 :
-        this._previewWidth ? PDFBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth)
+        PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth :
+            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
@@ -264,7 +262,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }}>
             <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
             <div style={{
-                width: `calc(${100 / scale}% - ${(this.sidebarWidth() + PDFBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
+                width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
                 height: `${100 / scale}%`,
                 transform: `scale(${scale})`,
                 position: "absolute",
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 7f314bddc..7969546c5 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -621,9 +621,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     });
     sidebarWidth = () => !this.SidebarShown ? 0 :
-        this._previewWidth ? WebBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() /
-            NumCast(this.layoutDoc.nativeWidth)
+        WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth :
+            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
 
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
@@ -715,7 +714,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 <div className="webBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
                 <div className="webBox-container" style={{
                     position: "absolute",
-                    width: `calc(${100 / scale}% - ${(this.sidebarWidth() + WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
+                    width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
                     transform: `scale(${scale})`,
                     pointerEvents
                 }} onContextMenu={this.specificContextMenu}>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1253cf9c7..c932259de 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -346,6 +346,7 @@ export namespace Doc {
         return GetT(doc, "system", "boolean", true);
     }
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
+        if (key.startsWith("_")) key = key.substring(1);
         const hasProto = doc.proto instanceof Doc;
         const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
         const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
@@ -962,7 +963,11 @@ export namespace Doc {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
     }
     export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) { return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeWidth"], useWidth ? doc[WidthSym]() : 0)); }
-    export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) { return !doc ? 0 : NumCast(doc._nativeHeight, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeHeight"], useHeight ? doc[HeightSym]() : 0)); }
+    export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
+        const dheight = doc ? NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeHeight"], useHeight ? doc[HeightSym]() : 0) : 0;
+        const nheight = doc ? Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]() / doc[WidthSym]() : 0;
+        return !doc ? 0 : NumCast(doc._nativeHeight, nheight || dheight);
+    }
     export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeWidth"] = width; }
     export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeHeight"] = height; }
 
-- 
cgit v1.2.3-70-g09d2


From d21354fefc9f5b343e51c4db67e0bae7d0c187ee Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 24 Mar 2022 10:06:14 -0400
Subject: fixing data_url uploading to have a no suffix option.

---
 src/client/views/PropertiesButtons.tsx | 4 ++--
 src/client/views/nodes/VideoBox.tsx    | 7 +++++++
 src/client/views/nodes/WebBox.tsx      | 4 ++--
 3 files changed, 11 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index dd6448654..24857d8ee 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -102,9 +102,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             else {
                 document.body.focus(); // so that we can access the clipboard without an error
                 setTimeout(() =>
-                    pasteImageBitmap((data: any, error: any) => {
+                    pasteImageBitmap((data_url: any, error: any) => {
                         error && console.log(error);
-                        data && VideoBox.convertDataUri(data, doc[Id] + "-thumb-frozen", true).then(
+                        data_url && VideoBox.convertDataUri(data_url, doc[Id] + "-thumb-frozen", true).then(
                             returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename));
                     }));
             }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 33fdc4935..5019d5691 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -252,6 +252,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
     }
 
+    /**
+     * Uploads an image buffer to the server and stores with specified filename.  by default the image
+     * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
+     * @param imageUri the bytes of the image
+     * @param returnedFilename the base filename to store the image on the server
+     * @param nosuffix optionally suppress creating multiple resolution images
+     */
     public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false) {
         try {
             const posting = Utils.prepend("/uploadURI");
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 7969546c5..e6acddf23 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -144,8 +144,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
                             NumCast(this.layoutDoc._scrollTop)
                         ).then
-                            ((dataUrl: any) => {
-                                VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
+                            ((data_url: any) => {
+                                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
                                     returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
                             })
                             .catch(function (error: any) {
-- 
cgit v1.2.3-70-g09d2


From c534619229b0f1b7d1ef5606d76f6e4829743379 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 24 Mar 2022 10:23:53 -0400
Subject: fixed scroll location of webpages when activated for first time.

---
 src/client/views/nodes/WebBox.tsx | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e6acddf23..3e3a6cfa5 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -103,6 +103,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return true;
     }
+    setScrollPos = (pos: number) => {
+        if (!this._outerRef.current || this._outerRef.current.scrollHeight < pos) {
+            setTimeout(() => this.setScrollPos(pos), 250);
+        } else {
+            this._outerRef.current.scrollTop = pos;
+            this._initialScroll = undefined;
+        }
+    }
     async componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
@@ -118,11 +126,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     this._webPageHasBeenRendered = true;
                     setTimeout(action(() => {
                         this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0);
-                        if (this._initialScroll !== undefined && this._outerRef.current) {
-                            setTimeout(() => {
-                                this._outerRef.current!.scrollTop = this._initialScroll!;
-                                this._initialScroll = undefined;
-                            });
+                        if (this._initialScroll !== undefined) {
+                            this.setScrollPos(this._initialScroll);
                         }
                     }));
                 } else if (!this.props.isContentActive() &&
-- 
cgit v1.2.3-70-g09d2


From af37e51657913a4f1cc1536f7d7ad9f991d4dc32 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 24 Mar 2022 11:34:53 -0400
Subject: fixes for webbox scroll bars and resizer divider to show up properly

---
 src/client/views/nodes/WebBox.scss | 10 ++++++++--
 src/client/views/nodes/WebBox.tsx  | 15 ++++++++++-----
 2 files changed, 18 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 7dc970496..9d8825ad1 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,10 +5,16 @@
     height: 100%;
     position: relative;
     display: flex;
-    .webBox-background { 
+    .webBox-sideResizer { 
+        position: absolute;
         width: 100%;
         height: 100%;
         cursor: ew-resize;
+        background: yellow;
+    }
+    .webBox-background { 
+        width: 100%;
+        height: 100%;
         background: lightGray;
     }
 
@@ -193,7 +199,7 @@
         }
 
         div.webBox-outerContent::-webkit-scrollbar-thumb {
-            display: none;
+            cursor: nw-resize;
         }
     }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e6acddf23..495f6a7ba 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -666,7 +666,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
     showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
@@ -711,10 +711,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return (
             <div className="webBox" ref={this._mainCont}
                 style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} >
-                <div className="webBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+                <div className="webBox-background" />
                 <div className="webBox-container" style={{
-                    position: "absolute",
-                    width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
+                    width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : (this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
                     transform: `scale(${scale})`,
                     pointerEvents
                 }} onContextMenu={this.specificContextMenu}>
@@ -751,6 +750,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             annotationLayer={this._annotationLayer.current}
                             mainCont={this._mainCont.current} />}
                 </div >
+                <div className="webBox-sideResizer" style={{
+                    display: this.SidebarShown ? undefined : "none",
+                    width: WebBox.sidebarResizerWidth,
+                    left: `calc(100% - ${this.sidebarWidth() - WebBox.sidebarResizerWidth}px)`
+                }}
+                    onPointerDown={e => this.sidebarBtnDown(e, false)} />
                 <SidebarAnnos ref={this._sidebarRef}
                     {...this.props}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
@@ -759,7 +764,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
                     setHeight={emptyFunction}
-                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
+                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.scaling?.() || 1)}
                     showSidebar={this.SidebarShown}
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
-- 
cgit v1.2.3-70-g09d2


From e18c52d3937d4dccc2e9284b32bb86b1e7dac45b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 25 Mar 2022 12:10:50 -0400
Subject: fixed annotations on web pages to be scaled to the correct location.

---
 src/client/views/nodes/WebBox.tsx  | 25 +++++++++++++------------
 src/client/views/pdf/PDFViewer.tsx | 11 +++++++----
 2 files changed, 20 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index eca0138a0..c12059943 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -742,18 +742,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         </div>
                     </div>
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                        <MarqueeAnnotator rootDoc={this.rootDoc}
-                            iframe={this.isFirefox() ? this.iframeClick : undefined}
-                            iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
-                            anchorMenuClick={this.anchorMenuClick}
-                            scrollTop={0}
-                            down={this._marqueeing} scaling={returnOne}
-                            addDocument={this.addDocumentWrapper}
-                            docView={this.props.docViewPath().lastElement()}
-                            finishMarquee={this.finishMarquee}
-                            savedAnnotations={this._savedAnnotations}
-                            annotationLayer={this._annotationLayer.current}
-                            mainCont={this._mainCont.current} />}
+                        <div style={{ transformOrigin: "top left", transform: `scale(${1 / scale})` }}>
+                            <MarqueeAnnotator rootDoc={this.rootDoc}
+                                iframe={this.isFirefox() ? this.iframeClick : undefined}
+                                iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
+                                anchorMenuClick={this.anchorMenuClick}
+                                scrollTop={0}
+                                down={this._marqueeing} scaling={returnOne}
+                                addDocument={this.addDocumentWrapper}
+                                docView={this.props.docViewPath().lastElement()}
+                                finishMarquee={this.finishMarquee}
+                                savedAnnotations={this._savedAnnotations}
+                                annotationLayer={this._annotationLayer.current}
+                                mainCont={this._mainCont.current} /> </div>}
                 </div >
                 <div className="webBox-sideResizer" style={{
                     display: this.SidebarShown ? undefined : "none",
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1856c5353..651b0401b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -580,14 +580,17 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 {this.overlayInfo}
                 {this.standinViews}
                 {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator rootDoc={this.props.rootDoc} scrollTop={0} down={this._marqueeing}
+                    <MarqueeAnnotator rootDoc={this.props.rootDoc}
+                        getPageFromScroll={this.getPageFromScroll}
                         anchorMenuClick={this.props.anchorMenuClick}
+                        scrollTop={0}
+                        down={this._marqueeing}
                         addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
-                        finishMarquee={this.finishMarquee}
                         docView={this.props.docViewPath().lastElement()}
-                        getPageFromScroll={this.getPageFromScroll}
+                        finishMarquee={this.finishMarquee}
                         savedAnnotations={this._savedAnnotations}
-                        annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
+                        annotationLayer={this._annotationLayer.current}
+                        mainCont={this._mainCont.current} />}
             </div>
         </div>;
     }
-- 
cgit v1.2.3-70-g09d2


From 6c7101d4f69dd79a83a48d04356748213c38a435 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 13:31:33 -0400
Subject: making layout icons work better.

---
 src/client/documents/Documents.ts                  |  4 ++-
 src/client/util/CurrentUserUtils.ts                | 15 +++++---
 src/client/util/DragManager.ts                     |  6 ++--
 src/client/views/DocumentDecorations.tsx           | 20 ++++++++---
 src/client/views/InkingStroke.tsx                  |  6 +++-
 src/client/views/StyleProvider.tsx                 |  7 ++--
 .../views/collections/CollectionCarouselView.tsx   |  4 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 40 ++++++++++++++++++++++
 .../collectionFreeForm/MarqueeView.scss            |  1 +
 src/client/views/nodes/DocumentView.tsx            |  7 ++--
 src/client/views/nodes/ImageBox.tsx                |  6 ++--
 src/client/views/nodes/LabelBigText.js             | 33 +++++++++++++-----
 src/client/views/nodes/LabelBox.tsx                |  5 +--
 src/client/views/nodes/WebBox.tsx                  |  4 +--
 src/fields/Doc.ts                                  |  2 +-
 15 files changed, 123 insertions(+), 37 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bb60586eb..652f8e7e0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -139,6 +139,8 @@ export class DocumentOptions {
     _itemIndex?: number; // which item index the carousel viewer is showing
     _showSidebar?: boolean;  //whether an annotationsidebar should be displayed for text docuemnts
     _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document)
+    _minFontSize?: number; // minimum font size for labelBoxes
+    _maxFontSize?: number; // maximum font size for labelBoxes
     _columnWidth?: number;
     _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
     _fontSize?: string;
@@ -441,7 +443,7 @@ export namespace Docs {
             }],
             [DocumentType.LABEL, {
                 layout: { view: LabelBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
+                options: { links: "@links(self)", _singleLine: true }
             }],
             [DocumentType.EQUATION, {
                 layout: { view: EquationBox, dataField: defaultDataKey },
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index af731ce9f..bc4dbcb2e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -9,7 +9,7 @@ import { RichTextField } from "../../fields/RichTextField";
 import { listSpec } from "../../fields/Schema";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
-import { nullAudio } from "../../fields/URLField";
+import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -292,7 +292,7 @@ export class CurrentUserUtils {
         if (doc["template-icon-view"] === undefined) {
             const iconView = Docs.Create.LabelDocument({
                 title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimgray",
-                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             //  Docs.Create.TextDocument("", {
             //     title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" })
@@ -304,7 +304,8 @@ export class CurrentUserUtils {
         if (doc["template-icon-view-rtf"] === undefined) {
             const iconRtfView = Docs.Create.LabelDocument({
                 title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
-                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                _singleLine: false, _minFontSize: 18, _maxFontSize: 24,
+                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF);
             doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView);
@@ -319,14 +320,18 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 180 / 4, _height: 135 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
+            const proto = iconColView.proto as Doc;
+            proto["icon-nativeWidth"] = 180 / 4;
+            proto["icon-nativeHeight"] = 135 / 4;
+            proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png");
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
         if (doc["template-icons"] === undefined) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 411fc6d11..d6d04db9a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -380,8 +380,8 @@ export namespace DragManager {
                 }
             }
             const rect = ele.getBoundingClientRect();
-            const scaleX = rect.width / ele.offsetWidth;
-            const scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX;
+            const scaleX = rect.width / (ele.offsetWidth || rect.width);
+            const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX;
 
             elesCont.left = Math.min(rect.left, elesCont.left);
             elesCont.top = Math.min(rect.top, elesCont.top);
@@ -424,7 +424,7 @@ export namespace DragManager {
 
         const hideDragShowOriginalElements = (hide: boolean) => {
             dragLabel.style.display = hide ? "" : "none";
-            !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
+            //!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.forEach(ele => ele.hidden = hide);
         };
         options?.hideSource && hideDragShowOriginalElements(true);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d7ead713a..1487d5bb0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -144,10 +144,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         return true;
     }
 
+    _iconifyTimeout: NodeJS.Timeout | undefined;
     onCloseClick = () => {
-        const selected = SelectionManager.Views().slice();
-        SelectionManager.DeselectAll();
-        selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+        const views = SelectionManager.Views().slice().filter(v => v);
+        const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
+        const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
+        icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+        others.forEach(dv => dv.iconify());
+        others.length && (this._iconifyTimeout = setTimeout(() => {
+            views.forEach(view => SelectionManager.DeselectView(view));
+            this._iconifyTimeout = undefined;
+        }, 1000));
     }
     onMaximizeDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, () => {
@@ -171,7 +178,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             } else if (e.altKey) {    // open same document in new tab
                 CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
             } else {
-                LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document));
+                var openDoc = selectedDocs[0].props.Document;
+                if (openDoc.layoutKey === "layout_icon") {
+                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                    Doc.deiconifyView(openDoc);
+                }
+                LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
             }
         }
         SelectionManager.DeselectAll();
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 06671961d..5e589fdea 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -70,7 +70,11 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
+    screenToLocal = () => {
+        console.log("Scaling = " + this.props.scaling?.())
+        console.log("Stolocal = " + this.props.ScreenToLocalTransform().Scale)
+        return this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
+    }
 
     getAnchor = () => {
         console.log(document.activeElement);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 649ee8394..739670ecf 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -117,7 +117,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
-            let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""));
+            let docColor: Opt<string> =
+                StrCast(doc?.[fieldKey + "backgroundColor"],
+                    StrCast(doc?._backgroundColor,
+                        StrCast(props?.Document.backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : "")));
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
                 case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
@@ -127,7 +130,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break;
                 case DocumentType.SLIDER: break;
                 case DocumentType.EQUATION: docColor = docColor || "transparent"; break;
-                case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
+                case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined) || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : "transparent"; break;
                 case DocumentType.LINK: docColor = (isAnchor ? docColor : "") || "transparent"; break;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 467c2893f..abb4b6bc6 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -44,14 +44,14 @@ export class CollectionCarouselView extends CollectionSubView() {
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         const curDoc = this.childLayoutPairs?.[index];
-        const captionProps = { ...this.props, fieldKey: "caption" };
+        const captionProps = { ...OmitKeys(this.props, ["setHeight",]).omit, fieldKey: "caption" };
         const marginX = NumCast(this.layoutDoc["caption-xMargin"]);
         const marginY = NumCast(this.layoutDoc["caption-yMargin"]);
         const showCaptions = StrCast(this.layoutDoc._showCaption);
         return !(curDoc?.layout instanceof Doc) ? (null) :
             <>
                 <div className="collectionCarouselView-image" key="image">
-                    <DocumentView  {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit}
+                    <DocumentView  {...OmitKeys(this.props, ["setHeight", "NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit}
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
                         hideCaptions={showCaptions ? true : false}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b2697cf08..7ca6dfd93 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,6 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
+import { ImageField } from "../../../../fields/URLField";
 import { createSchema, listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
@@ -41,6 +42,8 @@ import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDo
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
+import { VideoBox } from "../../nodes/VideoBox";
+import { CreateImage } from "../../nodes/WebBoxRenderer";
 import { StyleLayers, StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
@@ -1411,6 +1414,42 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
+    clicks = 0;
+    updateIcon = () => {
+        if (this.clicks) {
+            console.log("UPDATE ICON");
+        }
+        this.clicks++;
+        this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString();
+        this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString();
+        var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!);
+        this.props.docViewPath().lastElement().ContentDiv!.style.width = "";
+        this.props.docViewPath().lastElement().ContentDiv!.style.height = "";
+        const nativeWidth = this.layoutDoc[WidthSym]();
+        const nativeHeight = this.layoutDoc[HeightSym]();
+
+        CreateImage(
+            "",
+            document.styleSheets,
+            htmlString?.replace(/"marqueeView"/g, '"marqueeView marqueeView2"'),
+            nativeWidth,
+            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+            NumCast(this.layoutDoc._scrollTop)
+        ).then
+            ((data_url: any) => {
+                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then(
+                    returnedfilename => setTimeout(action(() => {
+
+                        this.dataDoc.icon = new ImageField(returnedfilename);
+                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
+                    }), 500));
+            })
+            .catch(function (error: any) {
+                console.error('oops, something went wrong!', error);
+            });
+    }
+
     componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
         this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
@@ -1476,6 +1515,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
         !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 62510ce9d..41e4d6b6a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -10,6 +10,7 @@
     user-select: none;
 }
 
+
 .marqueeView:focus-within {
     overflow: hidden;
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 103c3624d..185eafa4a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -80,6 +80,7 @@ export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>
 export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void;
 export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
 export interface DocComponentView {
+    updateIcon?: () => void; // updates the icon representation of the document
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
@@ -1118,6 +1119,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
             onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
             style={{
+                display: "inline",
                 borderRadius: this.borderRounding,
                 pointerEvents: this.pointerEvents,
                 outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1233,6 +1235,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     public iconify() {
+        this.ComponentView?.updateIcon?.();
         const layoutKey = Cast(this.Document.layoutKey, "string", null);
         if (layoutKey !== "layout_icon") {
             this.switchViews(true, "icon");
@@ -1332,8 +1335,8 @@ export function deiconifyViewFunc(documentView: DocumentView) {
 }
 ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
     documentView.iconify();
-}
-);
+    documentView.select(false);
+});
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
     if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 17f95c1cc..fb9e86487 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -73,7 +73,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 scrSize: this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0],
                 selected: this.props.isSelected()
             }),
-            ({ forceFull, scrSize, selected }) => this._curSuffix = forceFull ? "_o" : scrSize < 100 ? "_s" : scrSize < 400 ? "_m" : scrSize < 800 || !selected ? "_l" : "_o",
+            ({ forceFull, scrSize, selected }) => this._curSuffix = this.fieldKey === "icon" ? "_m" : forceFull ? "_o" : scrSize < 100 ? "_s" : scrSize < 400 ? "_m" : scrSize < 800 || !selected ? "_l" : "_o",
             { fireImmediately: true, delay: 1000 });
         this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             ({ nativeSize, width }) => {
@@ -245,8 +245,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get nativeSize() {
         TraceMobx();
-        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], 500);
-        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1);
+        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"], 500));
+        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"], 1));
         const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"], 1);
         return { nativeWidth, nativeHeight, nativeOrientation };
     }
diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js
index db43551d8..78fe2839e 100644
--- a/src/client/views/nodes/LabelBigText.js
+++ b/src/client/views/nodes/LabelBigText.js
@@ -5,14 +5,14 @@ And from Jetroid/bigtext.js v1.0.0, September 2016
 
 Usage:
 BigText("#myElement",{
-	rotateText: {Number}, (null)
-	fontSizeFactor: {Number}, (0.8)
-	maximumFontSize: {Number}, (null)
-	limitingDimension: {String}, ("both")
-	horizontalAlign: {String}, ("center")
-	verticalAlign: {String}, ("center")
-	textAlign: {String}, ("center")
-	whiteSpace: {String}, ("nowrap")
+    rotateText: {Number}, (null)
+    fontSizeFactor: {Number}, (0.8)
+    maximumFontSize: {Number}, (null)
+    limitingDimension: {String}, ("both")
+    horizontalAlign: {String}, ("center")
+    verticalAlign: {String}, ("center")
+    textAlign: {String}, ("center")
+    whiteSpace: {String}, ("nowrap")
 });
 
 
@@ -28,6 +28,8 @@ fontSizeFactor: This option is used to give some vertical spacing for letters th
 
 maximumFontSize: maximum font size to use.
 
+minimumFontSize: minimum font size to use.  if font is calculated smaller than this, text will be rendered at this size and wrapped
+
 limitingDimension: In which dimension the font size should be limited. Possible values: "width", "height" or "both". Defaults to both. Using this option with values different than "both" overwrites the element parent width or height.
 
 horizontalAlign: Where to align the text horizontally. Possible values: "left", "center", "right". Defaults to "center".
@@ -187,7 +189,20 @@ export default function BigText(element, options) {
         lineHeight = Math.floor(heightFactor * 1000);
 
     var fontSize = lineHeight * options.fontSizeFactor;
-    if (options.maximumFontSize !== null && fontSize > options.maximumFontSize) {
+    if (fontSize < options.minimumFontSize) {
+        parentStyle.display = "flex";
+        parentStyle.alignItems = "center";
+        style.whiteSpace = "pre-wrap";
+        style.textAlign = "center";
+        style.visibility = "";
+        style.fontSize = "18px";
+        style.lineHeight = "20px";
+        style.top = "";
+        style.left = "";
+        style.margin = "";
+        return element;
+    }
+    if (options.maximumFontSize && fontSize > options.maximumFontSize) {
         fontSize = options.maximumFontSize;
         lineHeight = fontSize / options.fontSizeFactor;
     }
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 0015f0b71..c689d9f40 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
-import { Cast, StrCast } from '../../../fields/Types';
+import { Cast, StrCast, NumCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
@@ -100,7 +100,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
                             BigText(r, {
                                 rotateText: null,
                                 fontSizeFactor: 1,
-                                maximumFontSize: null,
+                                minimumFontSize: NumCast(this.layoutDoc._minFontSize),
+                                maximumFontSize: NumCast(this.layoutDoc._maxFontSize),
                                 limitingDimension: "both",
                                 horizontalAlign: "center",
                                 verticalAlign: "center",
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c12059943..0fd193977 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -130,8 +130,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             this.setScrollPos(this._initialScroll);
                         }
                     }));
-                } else if (!this.props.isContentActive() &&
-                    !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && /// don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
+                } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
+                    !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
                     const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
                     if (this._iframe && !imageBitmap) {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ebdbae344..15df673f3 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1119,7 +1119,7 @@ export namespace Doc {
         return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
 
-    export function deiconifyView(doc: any) {
+    export function deiconifyView(doc: Doc) {
         StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
     }
 
-- 
cgit v1.2.3-70-g09d2


From cb59842fcb79af7ef72443e08c6a479c3f0af343 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 25 Apr 2022 20:20:48 -0400
Subject:  changed freeformview's with engines to not allow children to be
 edited using setHeight prop since engine layouts are temporary and shouldn't
 edit the doc.  fixed tags to work with pivot view. cleaned up pile views to
 be less hacky..

---
 src/client/documents/Documents.ts                  | 27 +++++++++++++---------
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/SidebarAnnos.tsx                  |  2 +-
 .../views/collections/CollectionPileView.tsx       |  4 ++--
 .../views/collections/CollectionStackingView.tsx   |  6 ++---
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/TreeView.tsx          |  1 +
 .../CollectionFreeFormLayoutEngines.tsx            | 26 +++++++++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  5 +---
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 17 +++++++-------
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  1 +
 src/client/views/nodes/WebBox.tsx                  |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 19 files changed, 62 insertions(+), 48 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 49e53a214..a19530c92 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -14,7 +14,7 @@ import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Upload } from "../../server/SharedMediaTypes";
-import { OmitKeys, Utils } from "../../Utils";
+import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
 import { YoutubeBox } from "../apis/youtube/YoutubeBox";
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
@@ -1352,26 +1352,31 @@ export namespace DocUtils {
         if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
     }
 
-    export function pileup(docList: Doc[], x?: number, y?: number, create: boolean = true) {
+    export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
         let w = 0, h = 0;
         runInAction(() => {
             docList.forEach(d => {
                 DocUtils.iconify(d);
-                w = Math.max(d[WidthSym](), w);
-                h = Math.max(d[HeightSym](), h);
+                w = Math.max(NumCast(d._width), w);
+                h = Math.max(NumCast(d._height), h);
             });
-            h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away.  so this is a fallback hack to try to do something reasonable
             docList.forEach((d, i) => {
-                d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2;
-                d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2;
+                d.x = Math.cos(Math.PI * 2 * i / docList.length) * size;
+                d.y = Math.sin(Math.PI * 2 * i / docList.length) * size;
+                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+            });
+            const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0);
+            docList.forEach((d, i) => {
+                d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2);
+                d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2);
                 d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - 55, y: (y || 0) - 55, _width: 110, _height: 100, });
-            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
-            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
-            newCollection._width = newCollection._height = 110;
+            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, });
+            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
+            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
+            newCollection._width = newCollection._height = size * 2;
             newCollection._jitterRotation = 10;
             return newCollection;
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a67cb3014..68b4710ed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -297,6 +297,7 @@ export class MainView extends React.Component {
             searchFilterDocs={returnEmptyDoclist}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
+            suppressSetHeight={true}
             renderDepth={-1}
         />;
     }
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index fae385660..43a02d029 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -88,7 +88,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
     showTitle = () => "title";
-    setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
+    setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 8832b0f4a..4489601db 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -74,13 +74,13 @@ export class CollectionPileView extends CollectionSubView() {
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
             this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
-            DocUtils.pileup(this.childDocs, undefined, undefined, false);
+            DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
             this.layoutDoc._panX = 0;
             this.layoutDoc._panY = -10;
             this.props.Document._pileLayoutEngine = 'pass';
         } else {
             const defaultSize = 25;
-            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
+            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 250);
             !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
             if (this.layoutEngine() === 'pass') {
                 this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8634ea139..509005b45 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -141,7 +141,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             () => this.layoutDoc._columnHeaders = new List()
         );
         this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                 this.headerMargin + (this.isStackingView ?
                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
                     this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
@@ -408,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                                 Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                             if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
-                                this.props.setHeight(height);
+                                this.props.setHeight?.(height);
                             }
                         }
                     }));
@@ -458,7 +458,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            this.props.setHeight(this.headerMargin + height);
+                            this.props.setHeight?.(this.headerMargin + height);
                         }
                     }));
                     this.observer.observe(ref);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5bdd0c0ac..30e0adf24 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -453,7 +453,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!,);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 76da96174..41970eb96 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -88,7 +88,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
             const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
-            this.props.setHeight(bodyHeight + titleHeight);
+            this.props.setHeight?.(bodyHeight + titleHeight);
         }
     }
     unobserveHeight = (ref: any) => {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6213850d8..b9710b3f5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -30,6 +30,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { KeyValueBox } from '../nodes/KeyValueBox';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 598960af7..16c7df311 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -111,17 +111,17 @@ export function computerStarburstLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
     engineProps: any
 ) {
-    const mustFit = pivotDoc[WidthSym]() !== panelDim[0];
+    const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
-    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume a icon sized at 75
-    const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
+    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
+    const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? mustFit ? panelDim[0] * .33 : 75 : 400; // assume a icon sized at 10750
+        const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
         const deg = i / childPairs.length * Math.PI * 2;
         docMap.set(layout[Id], {
-            x: Math.cos(deg) * (burstRadius[0] / 3) - docSize / 2,
-            y: Math.sin(deg) * (burstRadius[1] / 3) - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
+            x: Math.cos(deg) * burstRadius[0] - docSize / 2,
+            y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
             width: docSize,//layout[WidthSym](),
             height: docSize * layout[HeightSym]() / layout[WidthSym](),
             zIndex: NumCast(layout.zIndex),
@@ -129,7 +129,7 @@ export function computerStarburstLayout(
             replica: ""
         });
     });
-    const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+    const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
     return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
@@ -267,7 +267,13 @@ export function computePivotLayout(
     });
 
     const dividers = sortedPivotKeys.map((key, i) =>
-        ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
+    ({
+        type: "div", color: "lightGray",
+        x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
+        y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+        height: maxColHeight,
+        payload: pivotColumnGroups.get(key)!.filters
+    }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
@@ -404,7 +410,7 @@ function normalizeResults(
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
     const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
-    aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
+    aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
     if (Number.isNaN(scale)) scale = 1;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index eaef1b49c..a14405a0b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1158,6 +1158,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             replica={entry.replica}
             renderIndex={renderIndex}
             dataTransition={entry.transition}
+            suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
             ContainingCollectionDoc={this.props.Document}
@@ -1300,10 +1301,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return [] as ViewDefResult[];
     }
 
+    @computed get layoutEngine() { return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); }
     @computed get doInternalLayoutComputation() {
         TraceMobx();
         const newPool = new Map<string, PoolData>();
-        switch (this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine)) {
+        switch (this.layoutEngine) {
             case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
             case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
             case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3e35039e3..6097425e1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,9 +17,6 @@ import { StyleProp } from "../StyleProvider";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { DocumentType } from "../../documents/DocumentTypes";
-import { collectionSchema } from "../../../fields/documentSchemas";
-import { CollectionViewType } from "../collections/CollectionView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -174,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
                 width: this.panelWidth(),
-                height: !this.ShowTitle && this.layoutDoc.autoHeight && this.rootDoc.type === DocumentType.RTF ? undefined : this.panelHeight(),
+                height: this.panelHeight(),
                 transform: this.transform,
                 transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 005133eb0..d4e8ffc7f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -112,7 +112,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
     isSelected: (outsideReaction: boolean) => boolean,
     select: (ctrl: boolean) => void,
     scaling?: () => number,
-    setHeight: (height: number) => void,
+    setHeight?: (height: number) => void,
     layoutKey: string,
 }> {
     @computed get layout(): string {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 504a3afb6..20eabe6a9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,6 +114,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     isHovering?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -842,11 +843,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    setHeight = (height: number) => {
-        if (this.props.renderDepth !== -1) {
-            this.layoutDoc._height = height;
-        }
-    }
+    setHeight = (height: number) => this.layoutDoc._height = height;
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
@@ -890,7 +887,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 setContentView={this.setContentView}
                 scaling={this.contentScaling}
                 PanelHeight={this.panelHeight}
-                setHeight={this.setHeight}
+                setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                 isContentActive={this.isContentActive}
                 ScreenToLocalTransform={this.screenToLocal}
                 rootSelected={this.rootSelected}
@@ -1207,7 +1204,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
             returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
     }
-    @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any); }
+    @computed get shouldNotScale() {
+        return (this.fitWidth && !this.nativeWidth) ||
+            this.props.ContainingCollectionView?.collectionViewType === CollectionViewType.Time ||
+            this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+    }
     @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
     @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
@@ -1324,7 +1325,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
                         width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: (!this.props.ignoreAutoHeight && this.layoutDoc.autoHeight && this.layoutDoc.type === DocumentType.RTF) || isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 943b9f153..ae9acfe3f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -22,7 +22,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
-    setHeight: (height: number) => void;
+    setHeight?: (height: number) => void;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: string;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index eca2e3cfd..55f6d6059 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -195,6 +195,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             ContainingCollectionDoc={undefined}
                             ContainingCollectionView={undefined}
                             renderDepth={-1}
+                            suppressSetHeight={true}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
                             focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0fd193977..eeb7b925b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             autoHeight => {
                 if (autoHeight) {
                     this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index dc1c67bea..710270be4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1524,7 +1524,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (children) {
             const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-            if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+            if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                 const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
                 if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9aaa6e90f..92a69c02b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -123,7 +123,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             autoHeight => {
                 if (autoHeight) {
                     this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 15df673f3..63af8401c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1108,7 +1108,7 @@ export namespace Doc {
         if (typeof value === "string") {
             value = value.replace(`,${Utils.noRecursionHack}`, "");
         }
-        const fieldVal = doc[key];
+        const fieldVal = key === "#" ? (StrCast(doc.tags).includes(":#" + value + ":") ? StrCast(doc.tags) : undefined) : doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
-- 
cgit v1.2.3-70-g09d2


From f4b6942fc1cad1f7e7ec7552e22f6c56bc158a77 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 14:35:49 -0400
Subject: made creating a new thumbnail icon delete previous ones.  fixed bug
 where documents would re-render if an earlier docuent in the collection list
 was added/removed.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  7 +++----
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx    |  3 +--
 src/client/views/nodes/PDFBox.tsx                            |  2 +-
 src/client/views/nodes/VideoBox.tsx                          |  5 +++--
 src/client/views/nodes/WebBox.tsx                            |  2 +-
 src/server/ApiManagers/UploadManager.ts                      | 12 ++++++++----
 6 files changed, 17 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a14405a0b..bdae5a4f8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1147,7 +1147,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
 
-    getChildDocView(entry: PoolData, renderIndex: number) {
+    getChildDocView(entry: PoolData) {
         const childLayout = entry.pair.layout;
         const childData = entry.pair.data;
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
@@ -1156,7 +1156,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             Document={childLayout}
             renderDepth={this.props.renderDepth + 1}
             replica={entry.replica}
-            renderIndex={renderIndex}
             dataTransition={entry.transition}
             suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
@@ -1335,7 +1334,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const elements = computedElementData.slice();
         Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) =>
             elements.push({
-                ele: this.getChildDocView(entry[1], i),
+                ele: this.getChildDocView(entry[1]),
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
@@ -1436,7 +1435,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             NumCast(this.layoutDoc._scrollTop)
         ).then
             ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then(
+                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
                     returnedfilename => setTimeout(action(() => {
 
                         this.dataDoc.icon = new ImageField(returnedfilename);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 6097425e1..3f16d3c49 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
@@ -28,7 +28,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     jitterRotation: number;
     dataTransition?: string;
     replica: string;
-    renderIndex: number;
     CollectionFreeFormView: CollectionFreeFormView;
 }
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 54c4be24d..23749d479 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -93,7 +93,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"])
         ).then
             ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then(
+                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
                     returnedfilename => setTimeout(action(() => {
                         this.dataDoc.icon = new ImageField(returnedfilename);
                         this.dataDoc["icon-nativeWidth"] = nativeWidth;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c31d38b0d..e57cb1abe 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -53,14 +53,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
      * @param returnedFilename the base filename to store the image on the server
      * @param nosuffix optionally suppress creating multiple resolution images
      */
-    public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false) {
+    public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
         try {
             const posting = Utils.prepend("/uploadURI");
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
                     name: returnedFilename,
-                    nosuffix
+                    nosuffix,
+                    replaceRootFilename
                 },
                 json: true,
             });
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index eeb7b925b..6a3c6336d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -150,7 +150,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             NumCast(this.layoutDoc._scrollTop)
                         ).then
                             ((data_url: any) => {
-                                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
+                                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
                                     returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
                             })
                             .catch(function (error: any) {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index bfa07d47a..5e0cd67cc 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -3,7 +3,7 @@ import { Method, _success } from "../RouteManager";
 import * as formidable from 'formidable';
 import v4 = require('uuid/v4');
 const AdmZip = require('adm-zip');
-import { extname, basename, dirname } from 'path';
+import { extname, basename, dirname, } from 'path';
 import { createReadStream, createWriteStream, unlink, writeFile } from "fs";
 import { publicDirectory, filesDirectory } from "..";
 import { Database } from "../database";
@@ -13,10 +13,8 @@ import { AcceptableMedia, Upload } from "../SharedMediaTypes";
 import { normalize } from "path";
 import RouteSubscriber from "../RouteSubscriber";
 const imageDataUri = require('image-data-uri');
-import { isWebUri } from "valid-url";
-import { Opt } from "../../fields/Doc";
 import { SolrManager } from "./SearchManager";
-import { StringDecoder } from "string_decoder";
+let fs = require('fs');
 
 export enum Directory {
     parsed_files = "parsed_files",
@@ -260,10 +258,16 @@ export default class UploadManager extends ApiManager {
                 const uri = req.body.uri;
                 const filename = req.body.name;
                 const origSuffix = req.body.nosuffix ? SizeSuffix.None : SizeSuffix.Original;
+                const deleteFiles = req.body.replaceRootFilename;
                 if (!uri || !filename) {
                     res.status(401).send("incorrect parameters specified");
                     return;
                 }
+                if (deleteFiles) {
+                    const path = serverPathToFile(Directory.images, "");
+                    let regex = new RegExp(`${deleteFiles}.*`);
+                    fs.readdirSync(path).filter((f: any) => regex.test(f)).map((f: any) => fs.unlinkSync(path + f));
+                }
                 return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
                     const ext = extname(savedName).toLowerCase();
                     const { pngs, jpgs } = AcceptableMedia;
-- 
cgit v1.2.3-70-g09d2


From d3eac62cbc55f186d9e2d3cc47407087fdc03ec7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Apr 2022 00:21:27 -0400
Subject: fixed pushpin behavior when target is near top or bottom of
 scrollable document.

---
 src/Utils.ts                       | 6 +++---
 src/client/views/nodes/WebBox.tsx  | 2 +-
 src/client/views/pdf/PDFViewer.tsx | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 38eb51529..205f9379e 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -204,11 +204,11 @@ export namespace Utils {
         return { h: h, s: s, l: l };
     }
 
-    export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number) {
-        if (scrollTop + contextHgt < targetY + minSpacing + targetHgt) {
+    export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) {
+        if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) {
             return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
         }
-        if (scrollTop > targetY - minSpacing - targetHgt) {
+        if (scrollTop > Math.max(0, targetY - minSpacing - targetHgt)) {
             return Math.max(0, Math.floor(targetY - minSpacing - targetHgt));
         }
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6a3c6336d..5a39123b9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -243,7 +243,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1);
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1, this.getScrollHeight());
             if (scrollTo !== undefined) {
                 const focusSpeed = smooth ? 500 : 0;
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b58966173..b55b2cf44 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -181,7 +181,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight);
+            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined) {
                 focusSpeed = 500;
 
-- 
cgit v1.2.3-70-g09d2

