From 4823e1c7ceb0e58cdb515e2bb013632d81767ae3 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Tue, 5 Apr 2022 20:03:36 -0400
Subject: added basic grouping when exiting pen mode

---
 src/client/views/nodes/button/FontIconBox.tsx | 83 ++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index ca13590de..fc0cc87ca 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,17 +5,19 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, StrListCast } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, StrListCast, WidthSym } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { createSchema } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
-import { Utils } from '../../../../Utils';
+import { aggregateBounds, Utils } from '../../../../Utils';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
@@ -701,6 +703,80 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+    // if the global collection has documents
+    // const unallocInk: Doc[] = DocListCast(Doc.UserDoc().unallocatedInkDocs)
+    // if (unallocInk.length) {
+        // get collection freeform view from the ink documents
+        // with that, we can call gesture overlay getCollection on the view
+        // use getDocumentView 
+        // You would want to call: DocumentManager.Instance.getDocumentView
+        // const docView = DocumentManager.Instance.getDocumentView(unallocInk[0])
+        // if (docView) {
+        //     docView.props.ContainingCollectionView
+        // }
+
+        // easy way without backing up to the server
+        // 
+    CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+        const selected = ffView.unprocessedDocs;
+        ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true);
+        // loop through selected an get the bound
+        const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+        
+        selected.map(action(d => {
+            const x = NumCast(d.x);
+            const y = NumCast(d.y);
+            const width = d[WidthSym]();
+            const height = d[HeightSym]();
+            bounds.push({x, y, width, height});
+        }))
+
+        const aggregBounds = aggregateBounds(bounds, 0, 0);
+        const marqViewRef = ffView._marqueeViewRef.current;
+
+        // set the vals for bounds in marqueeView
+        if (marqViewRef) {
+            marqViewRef._downX = aggregBounds.x;
+            marqViewRef._downY = aggregBounds.y;
+            marqViewRef._lastX = aggregBounds.r;
+            marqViewRef._lastY = aggregBounds.b;
+        }        
+
+        selected.map(action(d => {
+            const dx = NumCast(d.x);
+            const dy = NumCast(d.y);
+            delete d.x;
+            delete d.y;
+            delete d.activeFrame;
+            delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+            delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+            // TODO: nda - actually calc the bounds
+            // get the bounds
+            // d.x = dx - aggregBounds.x;
+            // d.y = dy - aggregBounds.y;
+            
+            // d.x = dx - aggregBounds.x ;
+            // d.y = dy;
+            if (marqViewRef?.Bounds) {
+                d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds?.width / 2;
+                d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+            }
+            console.log(d[DataSym], d.x, d.y)
+            return d;
+        }));
+        ffView.props.removeDocument?.(selected);
+        // TODO: nda - this is the code to actually get a new grouped collection
+        // const newCollection = ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true);
+        const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+        console.log("newcoll:", newCollection?.[DataSym]);
+
+        // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+        newCollection && ffView.props.addDocument?.(newCollection);
+        ffView.unprocessedDocs = [];
+    });
+
+    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
@@ -716,6 +792,9 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     } else if (tool) { // pen or eraser
         if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
             Doc.UserDoc().activeInkTool = InkTool.None;
+        } else if (tool == "write") {
+            console.log("write mode selected - create groupDoc here!")
+            // 
         } else {
             Doc.UserDoc().activeInkTool = tool;
             GestureOverlay.Instance.InkShape = "";
-- 
cgit v1.2.3-70-g09d2


From ef63a072a586f51e8fa51b4684987491287540b2 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 7 Apr 2022 17:21:01 -0400
Subject: added grouping and write mode vs pen mode

---
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/views/GestureOverlay.tsx                |   5 +-
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  12 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/ImageBox.tsx                |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   2 +-
 src/client/views/nodes/VideoBox.tsx                |   2 +-
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 112 ++++++++++-----------
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/fields/InkField.ts                             |   3 +-
 webpack.config.js                                  |   2 +-
 14 files changed, 77 insertions(+), 79 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6cab8c100..f08f13bbc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1010,8 +1010,8 @@ export class CurrentUserUtils {
 
     static inkTools(doc: Doc) {
         const tools: Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' },
-            { title: "Mode", toolTip: "Mode (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' },
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' },
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' },
             { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
             // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 87b006a93..53b360e60 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -491,7 +491,10 @@ export class GestureOverlay extends Touchable {
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+            if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+                CurrentUserUtils.SelectedTool = InkTool.Write;
+            }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
             // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 9e8374605..c9f55a7bd 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -358,7 +358,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
         }
         if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
-            ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a694ca2b3..40164313c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -468,6 +468,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     case InkTool.Highlighter:
                         break;
                         // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
+                    case InkTool.Write:
+                        break;
                     case InkTool.Pen: 
                         // not the greatest solution
                         // want to have a currentInkDoc field
@@ -545,14 +547,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                // const currentInkDoc = Cast(this.dataDoc.currentInkDoc, Doc, null);
-                // if (true) {
                     console.log("doc exists")
-                    // Doc.AddDocToList(currentInkDoc.data as Doc, undefined, inkDoc)
-                    this.unprocessedDocs.push(inkDoc);
-                    CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
+                    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+                        this.unprocessedDocs.push(inkDoc);
+                        CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
+                    }
                     this.addDocument(inkDoc);
-                // } else this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
             case GestureUtils.Gestures.Box:
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index eeb2b653b..94f2d17de 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -651,7 +651,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         return <div className="marqueeView"
             style={{
                 overflow: StrCast(this.props.Document._overflow),
-                cursor: CurrentUserUtils.SelectedTool === InkTool.Pen ? "crosshair" : "pointer"
+                cursor: CurrentUserUtils.SelectedTool === InkTool.Pen || InkTool.Write ? "crosshair" : "pointer"
             }}
 
             onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7092b335c..2e822bb6a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -517,7 +517,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerDown = (e: React.PointerEvent): void => {
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -550,7 +550,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return;
+        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
 
         if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 0a4168698..a8da22b61 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -322,7 +322,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index aa2130af5..9bf7c2e8c 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 33fdc4935..a5b8967c4 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -549,7 +549,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 7ff47107e..54c49ecad 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -529,7 +529,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fc0cc87ca..7f3a667a9 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -13,6 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields
 import { WebField } from '../../../../fields/URLField';
 import { aggregateBounds, Utils } from '../../../../Utils';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -717,64 +718,56 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
 
         // easy way without backing up to the server
         // 
-    CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-        const selected = ffView.unprocessedDocs;
-        ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true);
-        // loop through selected an get the bound
-        const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-        
-        selected.map(action(d => {
-            const x = NumCast(d.x);
-            const y = NumCast(d.y);
-            const width = d[WidthSym]();
-            const height = d[HeightSym]();
-            bounds.push({x, y, width, height});
-        }))
-
-        const aggregBounds = aggregateBounds(bounds, 0, 0);
-        const marqViewRef = ffView._marqueeViewRef.current;
-
-        // set the vals for bounds in marqueeView
-        if (marqViewRef) {
-            marqViewRef._downX = aggregBounds.x;
-            marqViewRef._downY = aggregBounds.y;
-            marqViewRef._lastX = aggregBounds.r;
-            marqViewRef._lastY = aggregBounds.b;
-        }        
-
-        selected.map(action(d => {
-            const dx = NumCast(d.x);
-            const dy = NumCast(d.y);
-            delete d.x;
-            delete d.y;
-            delete d.activeFrame;
-            delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-            delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-            // TODO: nda - actually calc the bounds
-            // get the bounds
-            // d.x = dx - aggregBounds.x;
-            // d.y = dy - aggregBounds.y;
+    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            const selected = ffView.unprocessedDocs;
+            // loop through selected an get the bound
+            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
             
-            // d.x = dx - aggregBounds.x ;
-            // d.y = dy;
-            if (marqViewRef?.Bounds) {
-                d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds?.width / 2;
-                d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-            }
-            console.log(d[DataSym], d.x, d.y)
-            return d;
-        }));
-        ffView.props.removeDocument?.(selected);
-        // TODO: nda - this is the code to actually get a new grouped collection
-        // const newCollection = ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true);
-        const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
-        console.log("newcoll:", newCollection?.[DataSym]);
-
-        // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-        newCollection && ffView.props.addDocument?.(newCollection);
-        ffView.unprocessedDocs = [];
-    });
-
+            selected.map(action(d => {
+                const x = NumCast(d.x);
+                const y = NumCast(d.y);
+                const width = d[WidthSym]();
+                const height = d[HeightSym]();
+                bounds.push({x, y, width, height});
+            }))
+    
+            const aggregBounds = aggregateBounds(bounds, 0, 0);
+            const marqViewRef = ffView._marqueeViewRef.current;
+    
+            // set the vals for bounds in marqueeView
+            if (marqViewRef) {
+                marqViewRef._downX = aggregBounds.x;
+                marqViewRef._downY = aggregBounds.y;
+                marqViewRef._lastX = aggregBounds.r;
+                marqViewRef._lastY = aggregBounds.b;
+            }        
+    
+            selected.map(action(d => {
+                const dx = NumCast(d.x);
+                const dy = NumCast(d.y);
+                delete d.x;
+                delete d.y;
+                delete d.activeFrame;
+                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                // TODO: nda - actually calc the bounds
+                // calculate pos based on bounds
+                if (marqViewRef?.Bounds) {
+                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+                }
+                return d;
+            }));
+            ffView.props.removeDocument?.(selected);
+            // TODO: nda - this is the code to actually get a new grouped collection
+            const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+    
+            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+            newCollection && ffView.props.addDocument?.(newCollection);
+            ffView.unprocessedDocs = [];
+        });
+    }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
 
     if (checkResult) {
@@ -793,8 +786,9 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
         if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
             Doc.UserDoc().activeInkTool = InkTool.None;
         } else if (tool == "write") {
-            console.log("write mode selected - create groupDoc here!")
-            // 
+            console.log("write mode selected - create groupDoc here!", tool)
+            Doc.UserDoc().activeInkTool = tool;
+            GestureOverlay.Instance.InkShape = "";
         } else {
             Doc.UserDoc().activeInkTool = tool;
             GestureOverlay.Instance.InkShape = "";
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1856c5353..37fbd7a1d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -371,7 +371,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 31024e805..c34e8f93d 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -11,7 +11,8 @@ export enum InkTool {
     Pen = "pen",
     Highlighter = "highlighter",
     Eraser = "eraser",
-    Stamp = "stamp"
+    Stamp = "stamp",
+    Write = "write"
 }
 
 
diff --git a/webpack.config.js b/webpack.config.js
index 3fd00bcf3..5a954db19 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -49,7 +49,7 @@ module.exports = {
         repl: ["./src/debug/Repl.tsx", 'webpack-hot-middleware/client?reload=true'],
         test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
         inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
-        mobileInterface: ["./src/mobile/MobileMain.tsx", 'webpack-hot-middleware/client?reload=true'],
+        mobileInterface: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
     },
     devtool: "source-map",
     output: {
-- 
cgit v1.2.3-70-g09d2


From 40d746706d4ac3d5c3302d1fe66e6ce1685251f1 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 7 Apr 2022 17:21:51 -0400
Subject: removed redundant commit

---
 src/client/views/nodes/button/FontIconBox.tsx | 15 ---------------
 1 file changed, 15 deletions(-)

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

diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 7f3a667a9..4b7540e1d 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -704,20 +704,6 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
-    // if the global collection has documents
-    // const unallocInk: Doc[] = DocListCast(Doc.UserDoc().unallocatedInkDocs)
-    // if (unallocInk.length) {
-        // get collection freeform view from the ink documents
-        // with that, we can call gesture overlay getCollection on the view
-        // use getDocumentView 
-        // You would want to call: DocumentManager.Instance.getDocumentView
-        // const docView = DocumentManager.Instance.getDocumentView(unallocInk[0])
-        // if (docView) {
-        //     docView.props.ContainingCollectionView
-        // }
-
-        // easy way without backing up to the server
-        // 
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             const selected = ffView.unprocessedDocs;
@@ -751,7 +737,6 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
                 delete d.activeFrame;
                 delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
                 delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                // TODO: nda - actually calc the bounds
                 // calculate pos based on bounds
                 if (marqViewRef?.Bounds) {
                     d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-- 
cgit v1.2.3-70-g09d2


From b3424535cb746ff9fba35375d9abf07ba174dcf0 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Tue, 12 Apr 2022 18:07:49 -0400
Subject: made changes to ink grouping

---
 src/Utils.ts                                       |  6 ++
 src/client/views/GestureOverlay.tsx                | 12 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 30 --------
 src/client/views/nodes/button/FontIconBox.tsx      | 85 ++++++++++++++++++++--
 4 files changed, 93 insertions(+), 40 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 6519b5d13..8d71ca6eb 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -5,6 +5,12 @@ import { Socket } from 'socket.io';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
 import Color = require('color');
+import { InkTool } from './fields/InkField';
+import { action } from 'mobx';
+import { CurrentUserUtils } from './client/util/CurrentUserUtils';
+import { CollectionFreeFormView } from './client/views/collections/collectionFreeForm';
+import { WidthSym, HeightSym } from './fields/Doc';
+import { NumCast } from './fields/Types';
 
 export namespace Utils {
     export let DRAG_THRESHOLD = 4;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 54fb50db9..24df3925b 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -24,6 +24,7 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import { checkInksToGroup } from "./nodes/button/FontIconBox";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -82,6 +83,7 @@ export class GestureOverlay extends Touchable {
         const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches);
         const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches);
         const nt: (React.Touch | Touch)[] = Array.from(e.touches);
+        console.log("getNewTouches");
         this._hands.forEach((hand) => {
             for (let i = 0; i < e.targetTouches.length; i++) {
                 const pt = e.targetTouches.item(i);
@@ -108,6 +110,7 @@ export class GestureOverlay extends Touchable {
     }
 
     onReactTouchStart = (te: React.TouchEvent) => {
+        console.log("react touch start");
         document.removeEventListener("touchmove", this.onReactHoldTouchMove);
         document.removeEventListener("touchend", this.onReactHoldTouchEnd);
         if (RadialMenu.Instance?._display === true) {
@@ -204,6 +207,7 @@ export class GestureOverlay extends Touchable {
     }
 
     onReactTouchMove = (e: TouchEvent) => {
+        console.log("react touch move");
         const nts: any = this.getNewTouches(e);
         this._holdTimer && clearTimeout(this._holdTimer);
         this._holdTimer = undefined;
@@ -620,6 +624,7 @@ export class GestureOverlay extends Touchable {
                 if (!actionPerformed) {
                     const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
                     newPoints.pop();
+                    console.log("getting to bezier math");
                     const controlPoints: { X: number, Y: number }[] = [];
 
                     const bezierCurves = fitCurve(newPoints, 10);
@@ -630,14 +635,15 @@ export class GestureOverlay extends Touchable {
                         controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
                         controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
 
-
                     }
                     const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) +
                         (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
-                    this._points = controlPoints;
-
+                        this._points = controlPoints;
+                    
                     this.dispatchGesture(GestureUtils.Gestures.Stroke);
+                    // TODO: nda - check inks to group here
+                    checkInksToGroup();
                     
                 }
                 this._points = [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 40164313c..f927c7934 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -441,13 +441,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             document.removeEventListener("pointerup", this.onPenUp);
             const currentCol = DocListCast(this.rootDoc.currentInkDoc)
             const rootDocList = DocListCast(this.rootDoc.data);
-            console.log("rootDocList", rootDocList[rootDocList.length - 1]);
-            console.log("currentCol", currentCol);
-            // if (!currentCol[0].data) {
-            //     currentCol[0].data = [];
-            // }
-            // let docList = DocListCast(currentCol[0]);
-
             currentCol.push(rootDocList[rootDocList.length - 1]);
             console.log(currentCol);
 
@@ -471,28 +464,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     case InkTool.Write:
                         break;
                     case InkTool.Pen: 
-                        // not the greatest solution
-                        // want to have a currentInkDoc field
-                        // const freeformOptions: DocumentOptions = {
-                        //     x: 0,
-                        //     y: 0,
-                        //     _width: 1500,
-                        //     _height: 1000,
-                        //     _fitWidth: true,
-                        //     _backgroundColor: 'pink',
-                        //     _backgroundGridShow: false,
-                        //     title: `Stroke-Col`,
-                        // };
-                        // // TODO: nda - fix the null issues that occur here
-                        // let currentInkDoc = Docs.Create.FreeformDocument([], freeformOptions)
-                        // this.rootDoc.currentInkDoc = currentInkDoc;
-                        // // example of creating the list
-                        // this.addDocument(currentInkDoc)
-                        // // wtf ...
-                        // document.addEventListener("pointerup", this.onPenUp);
-                        
-                        // create a new collection 
-                        // list.push()
                         break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
                     case InkTool.Eraser:
                         document.addEventListener("pointermove", this.onEraserMove);
@@ -547,7 +518,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                    console.log("doc exists")
                     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
                         this.unprocessedDocs.push(inkDoc);
                         CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 4b7540e1d..862c16050 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { DataSym, Doc, DocListCast, HeightSym, StrListCast, WidthSym } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, LayoutSym, StrListCast, WidthSym } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { createSchema } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -697,15 +697,24 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 
 
-/** INK
- *      setActiveInkTool
- *      setStrokeWidth
- *      setStrokeColor
- **/
+export function checkInksToGroup() {
+    console.log("getting here to inks group");
+    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+           // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
+              const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
+                    console.log(inkDoc.x, inkDoc.y);
+            });
+        });
+    }
+}
 
-ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+export function createInkGroup(inksToGroup?: Doc[]) {
+    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
             // loop through selected an get the bound
             const bounds: { x: number, y: number, width?: number, height?: number }[] = []
@@ -750,10 +759,72 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     
             // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
             newCollection && ffView.props.addDocument?.(newCollection);
+            // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+}
+
+
+/** INK
+ *      setActiveInkTool
+ *      setStrokeWidth
+ *      setStrokeColor
+ **/
+
+ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+    createInkGroup();
+    // if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    //     CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+    //         const selected = ffView.unprocessedDocs;
+    //         // loop through selected an get the bound
+    //         const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+            
+    //         selected.map(action(d => {
+    //             const x = NumCast(d.x);
+    //             const y = NumCast(d.y);
+    //             const width = d[WidthSym]();
+    //             const height = d[HeightSym]();
+    //             bounds.push({x, y, width, height});
+    //         }))
+    
+    //         const aggregBounds = aggregateBounds(bounds, 0, 0);
+    //         const marqViewRef = ffView._marqueeViewRef.current;
+    
+    //         // set the vals for bounds in marqueeView
+    //         if (marqViewRef) {
+    //             marqViewRef._downX = aggregBounds.x;
+    //             marqViewRef._downY = aggregBounds.y;
+    //             marqViewRef._lastX = aggregBounds.r;
+    //             marqViewRef._lastY = aggregBounds.b;
+    //         }        
+    
+    //         selected.map(action(d => {
+    //             const dx = NumCast(d.x);
+    //             const dy = NumCast(d.y);
+    //             delete d.x;
+    //             delete d.y;
+    //             delete d.activeFrame;
+    //             delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+    //             delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+    //             // calculate pos based on bounds
+    //             if (marqViewRef?.Bounds) {
+    //                 d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+    //                 d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+    //             }
+    //             return d;
+    //         }));
+    //         ffView.props.removeDocument?.(selected);
+    //         // TODO: nda - this is the code to actually get a new grouped collection
+    //         const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+    
+    //         // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+    //         newCollection && ffView.props.addDocument?.(newCollection);
+    //         ffView.unprocessedDocs = [];
+    //     });
+    // }
+    // CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
 
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
-- 
cgit v1.2.3-70-g09d2


From 98567999b9882ed093bb0d750504a1bd10aa9a94 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Tue, 12 Apr 2022 19:21:53 -0400
Subject: revert presbox & treeview onclick focus

---
 package-lock.json                             | 399 +-------------------------
 src/client/util/CurrentUserUtils.ts           |   2 +-
 src/client/util/DocumentManager.ts            |   4 +-
 src/client/views/MainView.tsx                 |   2 +-
 src/client/views/collections/TreeView.tsx     |  94 +++++-
 src/client/views/nodes/button/FontIconBox.tsx |   1 +
 6 files changed, 99 insertions(+), 403 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 0bfe79f5a..63d1b9d1e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -158,15 +158,9 @@
             }
         },
         "@babel/runtime-corejs3": {
-<<<<<<< HEAD
             "version": "7.17.7",
             "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.7.tgz",
             "integrity": "sha512-TvliGJjhxis5m7xIMvlXH/xG8Oa/LK0SCUCyfKD6nLi42n5fB4WibDJ0g9trmmBB6hwpMNx+Lzbxy9/4gpMaVw==",
-=======
-            "version": "7.17.2",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
-            "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
->>>>>>> master
             "requires": {
                 "core-js-pure": "^3.20.2",
                 "regenerator-runtime": "^0.13.4"
@@ -229,15 +223,9 @@
             }
         },
         "@discoveryjs/json-ext": {
-<<<<<<< HEAD
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
             "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
-=======
-            "version": "0.5.6",
-            "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
-            "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
->>>>>>> master
         },
         "@emotion/babel-plugin": {
             "version": "11.7.2",
@@ -272,15 +260,9 @@
                     "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
                 },
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -317,15 +299,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "escape-string-regexp": {
                     "version": "4.0.0",
@@ -422,15 +398,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "stylis": {
                     "version": "4.0.13",
@@ -458,39 +428,23 @@
             "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
         },
         "@emotion/react": {
-<<<<<<< HEAD
             "version": "11.8.2",
             "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz",
             "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==",
-=======
-            "version": "11.8.1",
-            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.1.tgz",
-            "integrity": "sha512-XGaie4nRxmtP1BZYBXqC5JGqMYF2KRKKI7vjqNvQxyRpekVAZhb6QqrElmZCAYXH1L90lAelADSVZC4PFsrJ8Q==",
->>>>>>> master
             "requires": {
                 "@babel/runtime": "^7.13.10",
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
                 "@emotion/serialize": "^1.0.2",
-<<<<<<< HEAD
-=======
-                "@emotion/sheet": "^1.1.0",
->>>>>>> master
                 "@emotion/utils": "^1.1.0",
                 "@emotion/weak-memoize": "^0.2.5",
                 "hoist-non-react-statics": "^3.3.1"
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -530,15 +484,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "stylis": {
                     "version": "4.0.13",
@@ -577,15 +525,9 @@
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -616,15 +558,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 }
             }
         },
@@ -707,15 +643,9 @@
             }
         },
         "@fortawesome/react-fontawesome": {
-<<<<<<< HEAD
             "version": "0.1.18",
             "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz",
             "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
-=======
-            "version": "0.1.17",
-            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz",
-            "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==",
->>>>>>> master
             "requires": {
                 "prop-types": "^15.8.1"
             },
@@ -786,15 +716,9 @@
             }
         },
         "@hig/theme-data": {
-<<<<<<< HEAD
             "version": "2.24.0",
             "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.24.0.tgz",
             "integrity": "sha512-/71T3kezuom7z60Bxyxwxo0oHqb2B59jCbdWrgRgUYorK0vO6zjFmQvRmPSgxq2i1bQxQLx12iuzW9cM494pMQ==",
-=======
-            "version": "2.23.1",
-            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.23.1.tgz",
-            "integrity": "sha512-Yca1iCIs8U1dpkLW1VNiKOJW9fCJdb70f1dc61+R+HO1QJ8jMBu5eOpmQE1NKotJmcazhsXpo6xC11yV1crE4w==",
->>>>>>> master
             "requires": {
                 "tinycolor2": "^1.4.1"
             }
@@ -867,30 +791,11 @@
                         "readable-stream": "^3.6.0"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "chownr": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-                    "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
-                },
->>>>>>> master
                 "emoji-regex": {
                     "version": "8.0.0",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
                     "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
                 },
-<<<<<<< HEAD
-=======
-                "fs-minipass": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-                    "requires": {
-                        "minipass": "^3.0.0"
-                    }
-                },
->>>>>>> master
                 "gauge": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -935,31 +840,6 @@
                         }
                     }
                 },
-<<<<<<< HEAD
-=======
-                "minipass": {
-                    "version": "3.1.6",
-                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
-                    "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
-                    "requires": {
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "minizlib": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-                    "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-                    "requires": {
-                        "minipass": "^3.0.0",
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "mkdirp": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-                    "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
-                },
->>>>>>> master
                 "node-fetch": {
                     "version": "2.6.7",
                     "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -968,17 +848,6 @@
                         "whatwg-url": "^5.0.0"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "nopt": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-                    "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
-                    "requires": {
-                        "abbrev": "1"
-                    }
-                },
->>>>>>> master
                 "npmlog": {
                     "version": "5.0.1",
                     "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
@@ -1016,22 +885,6 @@
                         "ansi-regex": "^5.0.1"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "tar": {
-                    "version": "6.1.11",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
-                    "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
-                    "requires": {
-                        "chownr": "^2.0.0",
-                        "fs-minipass": "^2.0.0",
-                        "minipass": "^3.0.0",
-                        "minizlib": "^2.1.1",
-                        "mkdirp": "^1.0.3",
-                        "yallist": "^4.0.0"
-                    }
-                },
->>>>>>> master
                 "tr46": {
                     "version": "0.0.3",
                     "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -1078,15 +931,9 @@
             },
             "dependencies": {
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -1200,15 +1047,9 @@
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -1228,15 +1069,6 @@
             "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
             "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
         },
-<<<<<<< HEAD
-=======
-        "@socket.io/component-emitter": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
-            "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
-            "dev": true
-        },
->>>>>>> master
         "@szmarczak/http-timer": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -1383,19 +1215,11 @@
             }
         },
         "@types/bson": {
-<<<<<<< HEAD
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz",
             "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==",
             "requires": {
                 "@types/node": "*"
-=======
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
-            "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
-            "requires": {
-                "bson": "*"
->>>>>>> master
             }
         },
         "@types/cacheable-request": {
@@ -1445,15 +1269,9 @@
             "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
         },
         "@types/connect": {
-<<<<<<< HEAD
             "version": "3.4.35",
             "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
             "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
-=======
-            "version": "3.4.33",
-            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
-            "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
->>>>>>> master
             "dev": true,
             "requires": {
                 "@types/node": "*"
@@ -1728,15 +1546,9 @@
             }
         },
         "@types/json-schema": {
-<<<<<<< HEAD
-            "version": "7.0.10",
-            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.10.tgz",
-            "integrity": "sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A=="
-=======
             "version": "7.0.9",
             "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
             "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
->>>>>>> master
         },
         "@types/keygrip": {
             "version": "1.0.2",
@@ -1745,15 +1557,9 @@
             "dev": true
         },
         "@types/keyv": {
-<<<<<<< HEAD
-            "version": "3.1.4",
-            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
-            "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
-=======
             "version": "3.1.3",
             "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
             "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
->>>>>>> master
             "requires": {
                 "@types/node": "*"
             }
@@ -1768,15 +1574,9 @@
             }
         },
         "@types/lodash": {
-<<<<<<< HEAD
-            "version": "4.14.180",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.180.tgz",
-            "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==",
-=======
             "version": "4.14.179",
             "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
             "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
->>>>>>> master
             "dev": true
         },
         "@types/mime": {
@@ -1810,10 +1610,7 @@
             "version": "3.6.20",
             "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
             "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
-<<<<<<< HEAD
             "dev": true,
-=======
->>>>>>> master
             "requires": {
                 "@types/bson": "*",
                 "@types/node": "*"
@@ -1929,15 +1726,9 @@
             }
         },
         "@types/prop-types": {
-<<<<<<< HEAD
-            "version": "15.7.3",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
-            "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
-=======
             "version": "15.7.4",
             "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
             "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
->>>>>>> master
         },
         "@types/prosemirror-commands": {
             "version": "1.0.4",
@@ -2076,15 +1867,9 @@
             }
         },
         "@types/react": {
-<<<<<<< HEAD
-            "version": "16.14.24",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.24.tgz",
-            "integrity": "sha512-e7U2WC8XQP/xfR7bwhOhNFZKPTfW1ph+MiqtudKb8tSV8RyCsovQx2sNVtKoOryjxFKpHPPC/yNiGfdeVM5Gyw==",
-=======
             "version": "16.14.23",
             "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
             "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
->>>>>>> master
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -2092,15 +1877,9 @@
             },
             "dependencies": {
                 "csstype": {
-<<<<<<< HEAD
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
                     "version": "3.0.10",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
                     "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 }
             }
         },
@@ -2178,15 +1957,9 @@
             }
         },
         "@types/react-redux": {
-<<<<<<< HEAD
-            "version": "7.1.23",
-            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
-            "integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
-=======
             "version": "7.1.22",
             "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
             "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
->>>>>>> master
             "requires": {
                 "@types/hoist-non-react-statics": "^3.3.0",
                 "@types/react": "*",
@@ -2209,10 +1982,6 @@
             "version": "6.8.9",
             "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.9.tgz",
             "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
-<<<<<<< HEAD
-            "dev": true,
-=======
->>>>>>> master
             "requires": {
                 "@types/react": "*"
             }
@@ -2383,15 +2152,9 @@
             "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
         },
         "@types/tough-cookie": {
-<<<<<<< HEAD
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
-            "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A=="
-=======
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
             "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
->>>>>>> master
         },
         "@types/typescript": {
             "version": "2.0.0",
@@ -2769,18 +2532,6 @@
             },
             "dependencies": {
                 "mime-db": {
-<<<<<<< HEAD
-                    "version": "1.52.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-                    "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
-                },
-                "mime-types": {
-                    "version": "2.1.35",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
-                    "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-                    "requires": {
-                        "mime-db": "1.52.0"
-=======
                     "version": "1.51.0",
                     "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
                     "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
@@ -2791,7 +2542,6 @@
                     "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
                     "requires": {
                         "mime-db": "1.51.0"
->>>>>>> master
                     }
                 }
             }
@@ -3245,15 +2995,9 @@
             "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
         },
         "asn1": {
-<<<<<<< HEAD
-            "version": "0.2.4",
-            "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-            "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-=======
             "version": "0.2.6",
             "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
             "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
->>>>>>> master
             "requires": {
                 "safer-buffer": "~2.1.0"
             }
@@ -3334,15 +3078,9 @@
             "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
         },
         "aws4": {
-<<<<<<< HEAD
-            "version": "1.9.1",
-            "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
-            "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
-=======
             "version": "1.11.0",
             "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
             "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
->>>>>>> master
         },
         "axios": {
             "version": "0.19.2",
@@ -3492,15 +3230,9 @@
             },
             "dependencies": {
                 "core-js": {
-<<<<<<< HEAD
-                    "version": "2.6.11",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
-                    "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
-=======
                     "version": "2.6.12",
                     "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
                     "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
->>>>>>> master
                 },
                 "regenerator-runtime": {
                     "version": "0.11.1",
@@ -3661,18 +3393,6 @@
             "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
             "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
         },
-<<<<<<< HEAD
-        "bindings": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-            "optional": true,
-            "requires": {
-                "file-uri-to-path": "1.0.0"
-            }
-        },
-=======
->>>>>>> master
         "bl": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
@@ -3859,21 +3579,12 @@
             "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
         },
         "browserslist": {
-<<<<<<< HEAD
-            "version": "4.20.2",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
-            "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
-            "requires": {
-                "caniuse-lite": "^1.0.30001317",
-                "electron-to-chromium": "^1.4.84",
-=======
             "version": "4.19.3",
             "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
             "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
             "requires": {
                 "caniuse-lite": "^1.0.30001312",
                 "electron-to-chromium": "^1.4.71",
->>>>>>> master
                 "escalade": "^3.1.1",
                 "node-releases": "^2.0.2",
                 "picocolors": "^1.0.0"
@@ -4119,15 +3830,9 @@
             "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
         },
         "caniuse-lite": {
-<<<<<<< HEAD
-            "version": "1.0.30001317",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz",
-            "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ=="
-=======
             "version": "1.0.30001312",
             "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
             "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
->>>>>>> master
         },
         "canvas": {
             "version": "2.9.0",
@@ -4236,16 +3941,6 @@
                 "upath": "^1.1.1"
             },
             "dependencies": {
-<<<<<<< HEAD
-                "fsevents": {
-                    "version": "1.2.13",
-                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
-                    "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-                    "optional": true,
-                    "requires": {
-                        "bindings": "^1.5.0",
-                        "nan": "^2.12.1"
-=======
                 "bindings": {
                     "version": "1.5.0",
                     "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -4737,7 +4432,6 @@
                             "bundled": true,
                             "optional": true
                         }
->>>>>>> master
                     }
                 }
             }
@@ -5739,7 +5433,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-<<<<<<< HEAD
         "d": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
@@ -5750,8 +5443,6 @@
                 "type": "^1.0.1"
             }
         },
-=======
->>>>>>> master
         "d3-array": {
             "version": "1.2.4",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
@@ -11529,12 +11220,7 @@
             "dependencies": {
                 "JSONStream": {
                     "version": "1.3.5",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
-=======
                     "bundled": true,
->>>>>>> master
                     "requires": {
                         "jsonparse": "^1.2.0",
                         "through": ">=2.2.7 <3"
@@ -12981,12 +12667,7 @@
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
-=======
                     "bundled": true
->>>>>>> master
                 },
                 "jsprim": {
                     "version": "1.4.2",
@@ -14371,12 +14052,7 @@
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
-=======
                     "bundled": true
->>>>>>> master
                 },
                 "string-width": {
                     "version": "2.1.1",
@@ -14409,24 +14085,14 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-=======
                     "bundled": true,
->>>>>>> master
                     "requires": {
                         "safe-buffer": "~5.2.0"
                     },
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-<<<<<<< HEAD
-                            "resolved": "",
-                            "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
-=======
                             "bundled": true
->>>>>>> master
                         }
                     }
                 },
@@ -16830,11 +16496,6 @@
             }
         },
         "react-table": {
-<<<<<<< HEAD
-            "version": "7.7.0",
-            "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.7.0.tgz",
-            "integrity": "sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA=="
-=======
             "version": "6.11.5",
             "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz",
             "integrity": "sha512-LM+AS9v//7Y7lAlgTWW/cW6Sn5VOb3EsSkKQfQTzOW8FngB1FUskLLNEVkAYsTX9LjOWR3QlGjykJqCE6eXT/g==",
@@ -16843,7 +16504,6 @@
                 "classnames": "^2.2.5",
                 "react-is": "^16.8.1"
             }
->>>>>>> master
         },
         "react-themeable": {
             "version": "1.1.0",
@@ -17531,37 +17191,6 @@
                 "object-assign": "^4.1.1"
             }
         },
-        "schema-utils": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
-            "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
-            "dev": true,
-            "requires": {
-                "@types/json-schema": "^7.0.8",
-                "ajv": "^6.12.5",
-                "ajv-keywords": "^3.5.2"
-            },
-            "dependencies": {
-                "ajv": {
-                    "version": "6.12.6",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-                    "dev": true,
-                    "requires": {
-                        "fast-deep-equal": "^3.1.1",
-                        "fast-json-stable-stringify": "^2.0.0",
-                        "json-schema-traverse": "^0.4.1",
-                        "uri-js": "^4.2.2"
-                    }
-                },
-                "ajv-keywords": {
-                    "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-                    "dev": true
-                }
-            }
-        },
         "scss-loader": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
@@ -18933,8 +18562,6 @@
                 "terser": "^5.7.2"
             },
             "dependencies": {
-<<<<<<< HEAD
-=======
                 "ajv": {
                     "version": "6.12.6",
                     "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -18964,7 +18591,6 @@
                         "ajv-keywords": "^3.5.2"
                     }
                 },
->>>>>>> master
                 "serialize-javascript": {
                     "version": "6.0.0",
                     "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
@@ -20182,15 +19808,9 @@
             "dev": true
         },
         "webpack": {
-<<<<<<< HEAD
-            "version": "5.70.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz",
-            "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==",
-=======
             "version": "5.69.1",
             "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
             "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
->>>>>>> master
             "dev": true,
             "requires": {
                 "@types/eslint-scope": "^3.7.3",
@@ -20202,7 +19822,7 @@
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.9.2",
+                "enhanced-resolve": "^5.8.3",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
@@ -20224,13 +19844,6 @@
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
                     "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
                     "dev": true
-<<<<<<< HEAD
-                },
-                "enhanced-resolve": {
-                    "version": "5.9.2",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
-                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
-=======
                 },
                 "ajv": {
                     "version": "6.12.6",
@@ -20251,10 +19864,9 @@
                     "dev": true
                 },
                 "enhanced-resolve": {
-                    "version": "5.9.1",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.1.tgz",
-                    "integrity": "sha512-jdyZMwCQ5Oj4c5+BTnkxPgDZO/BJzh/ADDmKebayyzNwjVX1AFCeGkOfxNx0mHi2+8BKC5VxUYiw3TIvoT7vhw==",
->>>>>>> master
+                    "version": "5.9.2",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
                     "dev": true,
                     "requires": {
                         "graceful-fs": "^4.2.4",
@@ -20272,8 +19884,6 @@
                     "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
                     "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
                     "dev": true
-<<<<<<< HEAD
-=======
                 },
                 "schema-utils": {
                     "version": "3.1.1",
@@ -20285,7 +19895,6 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
->>>>>>> master
                 },
                 "tapable": {
                     "version": "2.2.1",
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b62b9b3ac..af731ce9f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -350,7 +350,7 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Untitled Presentation", _viewType: CollectionViewType.Tree, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
+                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyCollection === undefined) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ad6d90bc3..93d0c6936 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -168,8 +168,8 @@ export class DocumentManager {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
         const docView = getFirstDocView(targetDoc, originatingDoc);
-        const wasHidden = targetDoc.hidden;  // 
-        if (wasHidden) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here.
+        const wasHidden = targetDoc?.hidden;  // 
+        if (wasHidden && targetDoc) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here.
         const focusAndFinish = (didFocus: boolean) => {
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dcb678046..3bf81d374 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -245,7 +245,7 @@ export class MainView extends React.Component {
             }));
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", childDontRegisterViews: true, _viewType: CollectionViewType.Tree, _forceActive: true, treeViewOpen: true, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "left");
         this.userDoc.activePresentation = pres;
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ff5c4bab1..751ae631b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -13,7 +13,7 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager } from '../../util/DocumentManager';
+import { DocumentManager, DocFocusOrOpen } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -31,6 +31,8 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { PresMovement } from '../nodes/trails/PresEnums';
+import { PresBox } from '../nodes/trails';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -112,13 +114,24 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
     @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    // @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAliases() { return this.childDocList("aliases"); }
     @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
     @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
     //      SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
 
+    @observable _presTimer!: NodeJS.Timeout;
+    @observable _presKeyEventsActive: boolean = false;
+
+    @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
+    // the selected item's index
+    @computed get itemIndex() { return NumCast(this.doc._itemIndex); }
+    // the item that's active 
+    @computed get activeItem() { return Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null); }
+    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+
     childDocList(field: string) {
         const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
         return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
@@ -546,7 +559,80 @@ export class TreeView extends React.Component<TreeViewProps> {
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
-    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+
+    // TODO: currently doc focus works, but can't seem to edit title
+    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+    onChildClick = () => {
+        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.())
+    }
+
+    //Regular click zoom to document on canvas
+    @action
+    selectElement = async (doc: Doc) => {
+        const context = Cast(doc.context, Doc, null);
+        this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
+        if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
+        else this.updateCurrentPresentation(context);
+
+        if (this.activeItem.setPosition &&
+            this.activeItem.y !== undefined &&
+            this.activeItem.x !== undefined &&
+            this.targetDoc.x !== undefined &&
+            this.targetDoc.y !== undefined) {
+            const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
+            const time = 10;
+            const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y);
+            const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x);
+
+            for (let i = 0; i < time; i++) {
+                this.targetDoc.x = NumCast(this.targetDoc.x) + xdiff / time;
+                this.targetDoc.y = NumCast(this.targetDoc.y) + ydiff / time;
+                await timer(0.1);
+            }
+        }
+    }
+
+    static keyEventsWrapper = (e: KeyboardEvent) => {
+        PresBox.Instance.keyEvents(e);
+    }
+
+    @action
+    updateCurrentPresentation = (pres?: Doc) => {
+        if (pres) Doc.UserDoc().activePresentation = pres;
+        else Doc.UserDoc().activePresentation = this.doc;
+        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+        this._presKeyEventsActive = true;
+        // PresBox.Instance = this;
+    }
+
+    //The function that is called when a document is clicked or reached through next or back.
+    //it'll also execute the necessary actions if presentation is playing.
+    public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
+        Doc.UnBrushAllDocs();
+        if (index >= 0 && index < this.childDocs.length) {
+            this.doc._itemIndex = index;
+            const activeItem: Doc = this.activeItem;
+            const targetDoc: Doc = this.targetDoc;
+
+            if (targetDoc) {
+                targetDoc && runInAction(() => {
+                    if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
+                    else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+                });
+                setTimeout(() => targetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+            }
+            if (targetDoc?.lastFrame !== undefined) {
+                targetDoc._currentFrame = 0;
+            }
+            if (!group) this._selectedArray.clear();
+            this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //TODO: needs more work on selected arrayUpdate selected array
+            // if (this.layoutDoc._viewType === "stacking" && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
+            // this.onHideDocument(); //Handles hide after/before
+        }
+    });
+
+
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index ca13590de..0c960a95b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -229,6 +229,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         const script = ScriptCast(this.rootDoc.script);
+        if (!script) { return null };
 
         let noviceList: string[] = [];
         let text: string | undefined;
-- 
cgit v1.2.3-70-g09d2


From ab5a0bd7cee9366dabf4ce40bde89b67730da2a5 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 14 Apr 2022 18:06:34 -0400
Subject: auto transcribes on grouping

---
 src/client/views/InkTranscription.tsx              | 50 ++++++++++------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 29 +++++++------
 src/client/views/nodes/button/FontIconBox.tsx      | 33 +++++++-------
 3 files changed, 57 insertions(+), 55 deletions(-)

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

diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 4957446dc..ea5d5a1ec 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -5,7 +5,7 @@ import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { InkData, InkField } from "../../fields/InkField";
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { Docs } from "../documents/Documents";
+import { DocumentType } from "../documents/DocumentTypes";
 import './InkTranscription.scss';
 
 export class InkTranscription extends React.Component {
@@ -15,8 +15,7 @@ export class InkTranscription extends React.Component {
     @observable _mathRef: any;
     @observable _textRegister: any;
     @observable _textRef: any;
-    private addDocument?: (doc: Doc | Doc[]) => boolean;
-    private bounds: { x: number, y: number, width: number, height: number } = { x: 0, y: 0, width: 0, height: 0 };
+    private currGroup?: Doc;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -27,9 +26,6 @@ export class InkTranscription extends React.Component {
     componentWillUnmount() {
         this._mathRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
         this._textRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
-
-        this._mathRef.removeEventListener('changed', (e: any) => this.callExport(this._mathRef));
-        this._textRef.removeEventListener('changed', (e: any) => this.callExport(this._textRef));
     }
 
     @action
@@ -58,15 +54,11 @@ export class InkTranscription extends React.Component {
                             }
                         }
                     }
-                },
-                triggers: {
-                    exportContent: 'DEMAND'
                 }
             }) : null;
         }
 
         r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
-        r.addEventListener('changed', (e: any) => this.callExport(this._mathRef));
 
         return this._mathRef = r;
     }
@@ -97,22 +89,22 @@ export class InkTranscription extends React.Component {
                             }
                         }
                     }
-                },
-                triggers: {
-                    exportContent: 'DEMAND'
                 }
             }) : null;
         }
 
         r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
-        r.addEventListener('changed', (e: any) => this.callExport(this._textRef));
 
         return this._textRef = r;
     }
 
-    transcribeInk = (inkdocs: Doc[], math: boolean, bounds: { x: number, y: number, width: number, height: number }, addDocument?: (doc: Doc | Doc[]) => boolean) => {
+    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => {
+        if (!groupDoc) return;
+
+        const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
+
         const strokes: InkData[] = [];
-        inkdocs.filter(i => Cast(i.data, InkField)).forEach(i => {
+        validInks.filter(i => Cast(i.data, InkField)).forEach(i => {
             // TODO: interpolate missing times stamps
             const d = Cast(i.data, InkField, null);
             const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
@@ -120,8 +112,7 @@ export class InkTranscription extends React.Component {
             strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top, time: pd.time })));
         });
 
-        this.addDocument = addDocument;
-        this.bounds = bounds;
+        this.currGroup = groupDoc;
 
         const pointerData = { "events": strokes.map(stroke => this.inkJSON(stroke)) };
         // console.log(JSON.stringify(pointerData));
@@ -135,12 +126,6 @@ export class InkTranscription extends React.Component {
         }
     }
 
-    callExport(ref: any) {
-        if (ref.editor.canExport) {
-            ref.editor.export_();
-        }
-    }
-
     inkJSON = (stroke: InkData) => {
         return {
             "pointerType": "PEN",
@@ -159,15 +144,24 @@ export class InkTranscription extends React.Component {
                 const latex = exports['application/x-latex'];
                 console.log(latex);
 
-                this.addDocument?.(Docs.Create.EquationDocument({ title: latex, x: this.bounds.x + this.bounds.width, y: this.bounds.y, _width: this.bounds.width, _height: this.bounds.height }));
+                if (this.currGroup) {
+                    this.currGroup.text = latex;
+                    this.currGroup.title = latex;
+                }
+
+                ref.editor.clear();
             }
             else if (exports['text/plain']) {
                 const text = exports['text/plain'];
                 console.log(text);
-                this.addDocument?.(Docs.Create.TextDocument(text, { title: text, x: this.bounds.x + this.bounds.width, y: this.bounds.y, _width: this.bounds.width, _height: this.bounds.height }));
-            }
 
-            ref.editor.clear();
+                if (this.currGroup) {
+                    this.currGroup.text = text;
+                    this.currGroup.title = text;
+                }
+
+                ref.editor.clear();
+            }
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c9465e7a4..023953658 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -98,7 +98,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     private _cachedPool: Map<string, PoolData> = new Map();
     private _lastTap = 0;
     private _batch: UndoManager.Batch | undefined = undefined;
-    
+
     // private isWritingMode: boolean = true;
     // private writingModeDocs: Doc[] = []; 
 
@@ -461,10 +461,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 switch (CurrentUserUtils.SelectedTool) {
                     case InkTool.Highlighter:
                         break;
-                        // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
+                    // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
                     case InkTool.Write:
                         break;
-                    case InkTool.Pen: 
+                    case InkTool.Pen:
                         break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
                     case InkTool.Eraser:
                         document.addEventListener("pointermove", this.onEraserMove);
@@ -509,7 +509,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
-    public unprocessedDocs: Doc[] =[];
+    public unprocessedDocs: Doc[] = [];
     public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
     @undoBatch
     onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
@@ -521,11 +521,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const strokes: PointData[] = !times || times.length == 0 ? points : points.map((pt, i) => { return { X: pt.X, Y: pt.Y, time: times[i] } });
                 const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), strokes,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
-                        this.unprocessedDocs.push(inkDoc);
-                        CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
-                    }
-                    this.addDocument(inkDoc);
+                if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+                    this.unprocessedDocs.push(inkDoc);
+                    CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
+                }
+                this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
             case GestureUtils.Gestures.Box:
@@ -1577,9 +1577,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @undoBatch
     @action
     transcribeStrokes = (math: boolean) => {
-        if (this.props.Document._isGroup) {
-            const inkdocs = this.childDocs.filter(s => s.type === DocumentType.INK);
-            InkTranscription.Instance.transcribeInk(inkdocs, math, { x: NumCast(this.layoutDoc.x) ?? 0, y: NumCast(this.layoutDoc.y) ?? 0, width: NumCast(this.layoutDoc._width) ?? 0, height: NumCast(this.layoutDoc._height) ?? 0 }, this.props.ContainingCollectionView?.addDocument);
+        if (this.props.Document._isGroup && this.props.Document.text) {
+            const text = StrCast(this.props.Document.text);
+
+            const lines = text.split("\n");
+            const width = 30 + 7 * Math.max(...lines.map(line => line.length));
+            const height = 30 + 15 * lines.length;
+
+            this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: text, x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: width, _height: height }));
         }
     }
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 862c16050..fc358f106 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -26,6 +26,7 @@ import { EditableView } from '../../EditableView';
 import { GestureOverlay } from '../../GestureOverlay';
 import { Colors } from '../../global/globalEnums';
 import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { InkTranscription } from '../../InkTranscription';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
@@ -702,9 +703,9 @@ export function checkInksToGroup() {
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-           // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
-              const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
-                    console.log(inkDoc.x, inkDoc.y);
+            // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
+            const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
+                console.log(inkDoc.x, inkDoc.y);
             });
         });
     }
@@ -718,26 +719,26 @@ export function createInkGroup(inksToGroup?: Doc[]) {
             const selected = ffView.unprocessedDocs;
             // loop through selected an get the bound
             const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-            
+
             selected.map(action(d => {
                 const x = NumCast(d.x);
                 const y = NumCast(d.y);
                 const width = d[WidthSym]();
                 const height = d[HeightSym]();
-                bounds.push({x, y, width, height});
+                bounds.push({ x, y, width, height });
             }))
-    
+
             const aggregBounds = aggregateBounds(bounds, 0, 0);
             const marqViewRef = ffView._marqueeViewRef.current;
-    
+
             // set the vals for bounds in marqueeView
             if (marqViewRef) {
                 marqViewRef._downX = aggregBounds.x;
                 marqViewRef._downY = aggregBounds.y;
                 marqViewRef._lastX = aggregBounds.r;
                 marqViewRef._lastY = aggregBounds.b;
-            }        
-    
+            }
+
             selected.map(action(d => {
                 const dx = NumCast(d.x);
                 const dy = NumCast(d.y);
@@ -756,11 +757,13 @@ export function createInkGroup(inksToGroup?: Doc[]) {
             ffView.props.removeDocument?.(selected);
             // TODO: nda - this is the code to actually get a new grouped collection
             const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
-    
+
             // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
             newCollection && ffView.props.addDocument?.(newCollection);
             // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
+
+            InkTranscription.Instance.transcribeInk(newCollection, selected, false);
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
@@ -780,7 +783,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     //         const selected = ffView.unprocessedDocs;
     //         // loop through selected an get the bound
     //         const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-            
+
     //         selected.map(action(d => {
     //             const x = NumCast(d.x);
     //             const y = NumCast(d.y);
@@ -788,10 +791,10 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     //             const height = d[HeightSym]();
     //             bounds.push({x, y, width, height});
     //         }))
-    
+
     //         const aggregBounds = aggregateBounds(bounds, 0, 0);
     //         const marqViewRef = ffView._marqueeViewRef.current;
-    
+
     //         // set the vals for bounds in marqueeView
     //         if (marqViewRef) {
     //             marqViewRef._downX = aggregBounds.x;
@@ -799,7 +802,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     //             marqViewRef._lastX = aggregBounds.r;
     //             marqViewRef._lastY = aggregBounds.b;
     //         }        
-    
+
     //         selected.map(action(d => {
     //             const dx = NumCast(d.x);
     //             const dy = NumCast(d.y);
@@ -818,7 +821,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
     //         ffView.props.removeDocument?.(selected);
     //         // TODO: nda - this is the code to actually get a new grouped collection
     //         const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
-    
+
     //         // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
     //         newCollection && ffView.props.addDocument?.(newCollection);
     //         ffView.unprocessedDocs = [];
-- 
cgit v1.2.3-70-g09d2


From 73d52c3c1a0b06e0180999cb876feb6025df31b2 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 28 Apr 2022 16:31:57 -0400
Subject: fixed timestamps and added some scaling

---
 src/client/documents/Documents.ts                  |  1 -
 src/client/views/GestureOverlay.tsx                | 22 +++----------
 src/client/views/InkTranscription.tsx              | 38 +++++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 15 ++++-----
 src/client/views/nodes/button/FontIconBox.tsx      | 10 +++---
 src/fields/InkField.ts                             |  3 +-
 src/pen-gestures/GestureUtils.ts                   |  3 +-
 7 files changed, 41 insertions(+), 51 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 884cc781d..aee9ef49d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1161,7 +1161,6 @@ export namespace DocUtils {
             created = Docs.Create.AudioDocument((field).url.href, resolved);
             layout = AudioBox.LayoutString;
         } else if (field instanceof InkField) {
-            console.log("Documents " + field.inkData)
             created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 5f0311d38..fc0a214a6 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -42,7 +42,6 @@ export class GestureOverlay extends Touchable {
     @observable private _menuY: number = -300;
     @observable private _pointerY?: number;
     @observable private _points: { X: number, Y: number }[] = [];
-    @observable private _timeStamps: number[] = [];
     @observable private _strokes: InkData[] = [];
     @observable private _palette?: JSX.Element;
     @observable private _clipboardDoc?: JSX.Element;
@@ -61,7 +60,6 @@ export class GestureOverlay extends Touchable {
     private pointerIdentifier?: number;
     private _hands: Map<number, React.Touch[]> = new Map<number, React.Touch[]>();
     private _holdTimer: NodeJS.Timeout | undefined;
-    private _strokeStartTime: number = 0;
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
@@ -299,7 +297,7 @@ export class GestureOverlay extends Touchable {
             pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v);
         }
         else {
-            console.log("not hand");
+            // console.log("not hand");
         }
         this.pointerIdentifier = pointer?.identifier;
 
@@ -425,7 +423,6 @@ export class GestureOverlay extends Touchable {
 
             this._strokes = [];
             this._points = [];
-            this._timeStamps = [];
             this._possibilities = [];
             document.removeEventListener("touchend", this.handleHandUp);
         }
@@ -509,8 +506,6 @@ export class GestureOverlay extends Touchable {
                 CurrentUserUtils.SelectedTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
-            this._timeStamps.push(0);
-            this._strokeStartTime = new Date().getTime();
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
             // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
@@ -519,7 +514,6 @@ export class GestureOverlay extends Touchable {
     @action
     onPointerMove = (e: PointerEvent) => {
         this._points.push({ X: e.clientX, Y: e.clientY });
-        this._timeStamps.push(new Date().getTime() - this._strokeStartTime);
 
         if (this._points.length > 1) {
             const B = this.svgBounds;
@@ -566,7 +560,6 @@ export class GestureOverlay extends Touchable {
             const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
             //push first points to so interactionUtil knows pointer is up
             this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
-            this._timeStamps.push(this._timeStamps[0]);
 
             const initialPoint = this._points[0.];
             const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
@@ -578,7 +571,6 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.InkToText:
                         this._strokes.push(new Array(...this._points));
                         this._points = [];
-                        this._timeStamps = [];
                         CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then((results) => {
                             const wordResults = results.filter((r: any) => r.category === "line");
                             const possibilities: string[] = [];
@@ -602,7 +594,6 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.IgnoreGesture:
                         this.dispatchGesture(GestureUtils.Gestures.Stroke);
                         this._points = [];
-                        this._timeStamps = [];
                         break;
                 }
             }
@@ -611,7 +602,6 @@ export class GestureOverlay extends Touchable {
                 this.makePolygon(this.InkShape, false);
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
-                this._timeStamps = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
                     Doc.UserDoc().activeInkTool = InkTool.None;
@@ -638,7 +628,7 @@ export class GestureOverlay extends Touchable {
                 if (!actionPerformed) {
                     const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
                     newPoints.pop();
-                    console.log("getting to bezier math");
+                    // console.log("getting to bezier math");
                     const controlPoints: { X: number, Y: number }[] = [];
 
                     const bezierCurves = fitCurve(newPoints, 10);
@@ -649,21 +639,19 @@ export class GestureOverlay extends Touchable {
                         controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
                         controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
 
-                    }   
+                    }
                     const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) +
                         (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
-                        this._points = controlPoints;
+                    this._points = controlPoints;
                     this.dispatchGesture(GestureUtils.Gestures.Stroke);
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
                 }
                 this._points = [];
-                this._timeStamps = [];
             }
         } else {
             this._points = [];
-            this._timeStamps = [];
         }
         CollectionFreeFormViewChrome.Instance?.primCreated();
     }
@@ -712,7 +700,6 @@ export class GestureOverlay extends Touchable {
             }
         }
         this._points = [];
-        this._timeStamps = [];
         switch (shape) {
             //must push an extra point in the end so InteractionUtils knows pointer is up.
             //must be (points[0].X,points[0]-1)
@@ -836,7 +823,6 @@ export class GestureOverlay extends Touchable {
                         gesture: gesture as any,
                         bounds: this.getBounds(stroke ?? this._points),
                         text: data,
-                        times: this._timeStamps
                     }
                 }
             )
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index ea5d5a1ec..a96cda743 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -1,12 +1,13 @@
-import { timeStamp } from 'console';
 import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
-import { Doc } from '../../fields/Doc';
+import { Doc, DocListCast } from '../../fields/Doc';
 import { InkData, InkField } from "../../fields/InkField";
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, NumCast } from '../../fields/Types';
 import { DocumentType } from "../documents/DocumentTypes";
 import './InkTranscription.scss';
+import { Utils } from '../../Utils';
+import { timesSeries } from 'async';
 
 export class InkTranscription extends React.Component {
     static Instance: InkTranscription;
@@ -16,6 +17,7 @@ export class InkTranscription extends React.Component {
     @observable _textRegister: any;
     @observable _textRef: any;
     private currGroup?: Doc;
+    private containingLayout?: Doc;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -98,24 +100,26 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => {
+    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean) => {
         if (!groupDoc) return;
 
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
         const strokes: InkData[] = [];
+        const times: number[] = [];
         validInks.filter(i => Cast(i.data, InkField)).forEach(i => {
-            // TODO: interpolate missing times stamps
             const d = Cast(i.data, InkField, null);
             const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
             const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
-            strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top, time: pd.time })));
+            strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
+            times.push(NumCast(i.creationDate, 1));
         });
 
         this.currGroup = groupDoc;
+        this.containingLayout = containingLayout;
 
-        const pointerData = { "events": strokes.map(stroke => this.inkJSON(stroke)) };
-        // console.log(JSON.stringify(pointerData));
+        const pointerData = { "events": strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) };
+        console.log(JSON.stringify(pointerData));
         const processGestures = false;
 
         if (math) {
@@ -126,13 +130,17 @@ export class InkTranscription extends React.Component {
         }
     }
 
-    inkJSON = (stroke: InkData) => {
+    inkJSON = (stroke: InkData, time: number) => {
+        const scale = NumCast(this.containingLayout?._viewScale, 1);
+        const panX = NumCast(this.containingLayout?._panX);
+        const panY = NumCast(this.containingLayout?._panY);
+
         return {
             "pointerType": "PEN",
             "pointerId": 1,
-            "x": stroke.map(point => point.X),
-            "y": stroke.map(point => point.Y),
-            "t": stroke.map(point => point.time),
+            "x": stroke.map(point => (point.X - panX) * scale),
+            "y": stroke.map(point => (point.Y - panY) * scale),
+            "t": new Array(stroke.length).fill(time),
             "p": new Array(stroke.length).fill(1.0)
         };
     }
@@ -142,7 +150,7 @@ export class InkTranscription extends React.Component {
         if (exports) {
             if (exports['application/x-latex']) {
                 const latex = exports['application/x-latex'];
-                console.log(latex);
+                // console.log(latex);
 
                 if (this.currGroup) {
                     this.currGroup.text = latex;
@@ -153,11 +161,11 @@ export class InkTranscription extends React.Component {
             }
             else if (exports['text/plain']) {
                 const text = exports['text/plain'];
-                console.log(text);
+                // console.log(text);
 
                 if (this.currGroup) {
                     this.currGroup.text = text;
-                    this.currGroup.title = text;
+                    this.currGroup.title = text.split("\n")[0];
                 }
 
                 ref.editor.clear();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 023953658..3c5cdb444 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -517,9 +517,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const times = ge.times;
-                const strokes: PointData[] = !times || times.length == 0 ? points : points.map((pt, i) => { return { X: pt.X, Y: pt.Y, time: times[i] } });
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), strokes,
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
                 if (CurrentUserUtils.SelectedTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
@@ -1578,13 +1576,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     transcribeStrokes = (math: boolean) => {
         if (this.props.Document._isGroup && this.props.Document.text) {
-            const text = StrCast(this.props.Document.text);
+            if (!math) {
+                const text = StrCast(this.props.Document.text);
 
-            const lines = text.split("\n");
-            const width = 30 + 7 * Math.max(...lines.map(line => line.length));
-            const height = 30 + 15 * lines.length;
+                const lines = text.split("\n");
+                const height = 30 + 15 * lines.length;
 
-            this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: text, x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: width, _height: height }));
+                this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
+            }
         }
     }
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fc358f106..aca393c1a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -262,7 +262,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
             }
         } catch (e) {
-            console.log(e);
+            // console.log(e);
         }
 
         // Get items to place into the list
@@ -699,13 +699,13 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 
 export function checkInksToGroup() {
-    console.log("getting here to inks group");
+    // console.log("getting here to inks group");
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
             const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
-                console.log(inkDoc.x, inkDoc.y);
+                // console.log(inkDoc.x, inkDoc.y);
             });
         });
     }
@@ -763,7 +763,7 @@ export function createInkGroup(inksToGroup?: Doc[]) {
             // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
 
-            InkTranscription.Instance.transcribeInk(newCollection, selected, false);
+            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false);
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
@@ -845,7 +845,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
         if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
             Doc.UserDoc().activeInkTool = InkTool.None;
         } else if (tool == "write") {
-            console.log("write mode selected - create groupDoc here!", tool)
+            // console.log("write mode selected - create groupDoc here!", tool)
             Doc.UserDoc().activeInkTool = tool;
             GestureOverlay.Instance.InkShape = "";
         } else {
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 6d24c6cbc..31fa6dfac 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -20,7 +20,6 @@ export enum InkTool {
 export interface PointData {
     X: number;
     Y: number;
-    time?: number;
 }
 
 export type Segment = Array<Bezier>;
@@ -85,7 +84,7 @@ export class InkField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}, time: ${i.time || 0}} `) + "])";
+        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}`) + "])";
     }
     [ToString]() {
         return "InkField";
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index cf59cb3c6..65f2bf80c 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -8,8 +8,7 @@ export namespace GestureUtils {
             readonly gesture: Gestures,
             readonly points: PointData[],
             readonly bounds: Rect,
-            readonly text?: any,
-            readonly times?: number[]
+            readonly text?: any
         ) { }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 4f6b6306803437851959e9ad214fd61a14207a20 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 28 Apr 2022 16:34:31 -0400
Subject: added basic code for subgrouping transcribed text

---
 src/client/util/InteractionUtils.tsx               |  1 +
 src/client/views/GestureOverlay.tsx                |  2 +-
 src/client/views/InkTranscription.tsx              | 71 +++++++++++++++++++++-
 src/client/views/LightboxView.tsx                  |  3 +-
 src/client/views/Touchable.tsx                     |  2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |  2 +
 src/client/views/nodes/button/FontIconBox.tsx      | 56 ++---------------
 8 files changed, 86 insertions(+), 55 deletions(-)

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

diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 675dff00b..e2c5fab64 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -357,6 +357,7 @@ export namespace InteractionUtils {
     }
 
     export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: React.Touch[], leniency: number): boolean {
+        console.log("getting here");
         for (const touch of newTouches) {
             if (touch) {
                 const oldTouch = oldTouches.get(touch.identifier);
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 5f0311d38..e689cd43c 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -212,7 +212,7 @@ export class GestureOverlay extends Touchable {
         const nts: any = this.getNewTouches(e);
         this._holdTimer && clearTimeout(this._holdTimer);
         this._holdTimer = undefined;
-
+        
         document.dispatchEvent(
             new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
                 {
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index ea5d5a1ec..3b464db10 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -5,6 +5,7 @@ import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { InkData, InkField } from "../../fields/InkField";
 import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { aggregateBounds } from '../../Utils';
 import { DocumentType } from "../documents/DocumentTypes";
 import './InkTranscription.scss';
 
@@ -15,12 +16,18 @@ export class InkTranscription extends React.Component {
     @observable _mathRef: any;
     @observable _textRegister: any;
     @observable _textRef: any;
+    private lastJiix: any;
     private currGroup?: Doc;
+    private wordsBoundMapping: any;
+    private inkDocs: Doc[];
+    private ffView: any;
 
     constructor(props: Readonly<{}>) {
         super(props);
 
         InkTranscription.Instance = this;
+        this.wordsBoundMapping = new Map<any, any>();
+        this.inkDocs = new Array<Doc>();
     }
 
     componentWillUnmount() {
@@ -98,8 +105,10 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => {
+    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: any) => {
         if (!groupDoc) return;
+        this.inkDocs = inkDocs;
+        this.ffView = ffView;
 
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
@@ -137,8 +146,20 @@ export class InkTranscription extends React.Component {
         };
     }
 
+    mmToPixel = (mm: number) => {
+        return ((96 * mm) / 25.4);
+    }
+
+    calcBounds = (coords: any) => {
+        // find max and min x values and subtract
+        const max = Math.max(...coords);
+        const min = Math.min(...coords);
+        return max - min;
+    }
+
     exportInk = (e: any, ref: any) => {
         const exports = e.detail.exports;
+        console.log(e);
         if (exports) {
             if (exports['application/x-latex']) {
                 const latex = exports['application/x-latex'];
@@ -152,12 +173,60 @@ export class InkTranscription extends React.Component {
                 ref.editor.clear();
             }
             else if (exports['text/plain']) {
+                if (exports['application/vnd.myscript.jiix']) {
+                    this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']);
+                    // console.log("lastJiix:", this.lastJiix);
+                    // TODO: nda - go through all the words and look at x,y values and figure out the selection
+                    this.lastJiix.words.forEach((word: any) => {
+                        // map each word to a new map                            
+                        this.wordsBoundMapping[word] = new Map<any, any>();
+                        if (word.items) {
+                            word.items.map((item: any) => {
+                                this.wordsBoundMapping.set(word, {xList: item.X, yList: item.Y, width: this.calcBounds(item.X), height: this.calcBounds(item.Y)});
+                            })
+                        }
+                    })
+                    // make groups for each of the words
+                    // loop through this.wordsBoundMapping
+
+                    this.wordsBoundMapping.forEach((value: any, key: any) => {
+                        const xList = value.xList;
+                        const yList = value.yList;
+                        const width = value.width;
+                        const height = value.height;
+                        const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+                        // loop through xList
+                        for (let i = 0; i < xList.length; i++) {
+                            const x = xList[i];
+                            const y = yList[i];
+                            const newBounds = { x: x, y: y };
+                            bounds.push(newBounds);
+                        }
+                        const aggregBounds = aggregateBounds(bounds, 0, 0);
+
+                        const marqViewRef = this.ffView?._marqueeViewRef.current;
+
+                        // set the vals for bounds in marqueeView
+                        if (marqViewRef) {
+                            marqViewRef._downX = aggregBounds.x;
+                            marqViewRef._downY = aggregBounds.y;
+                            marqViewRef._lastX = aggregBounds.r;
+                            marqViewRef._lastY = aggregBounds.b;
+                        }
+                    
+                    });
+
+                    // console.log(this.wordsBoundMapping);
+                }
                 const text = exports['text/plain'];
                 console.log(text);
 
                 if (this.currGroup) {
+                    console.log("curr grouping");
                     this.currGroup.text = text;
                     this.currGroup.title = text;
+                    console.log(this.lastJiix);
+                    console.log(this.currGroup);
                 }
 
                 ref.editor.clear();
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ec3bf6c18..c8f6b5f0b 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -5,7 +5,7 @@ import "normalize.css";
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
@@ -17,6 +17,7 @@ import "./LightboxView.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
 import { CollectionMenu } from './collections/CollectionMenu';
+import { utils } from 'mocha';
 
 interface LightboxViewProps {
     PanelWidth: number;
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 789756a78..c712e7ed3 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -127,11 +127,13 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
+        console.log("getting to handle1PointersMove");
         e.stopPropagation();
         e.preventDefault();
     }
 
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
+        console.log("getting to handle2PointersMove");
         e.stopPropagation();
         e.preventDefault();
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 023953658..7c11c252a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -702,9 +702,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerMove = (e: PointerEvent): void => {
+        console.log("this is onPointerMove");
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            console.log("is touch");
             Doc.UserDoc().activeInkTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
@@ -825,6 +825,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
+        console.log("getting here yeet");
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
@@ -836,6 +837,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         document.removeEventListener("pointerup", this.onPointerUp);
                         return;
                     }
+                    // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
                     this.pan(myTouches[0]);
                 }
             }
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 7e6ca4248..f9d2b9917 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -21,6 +21,8 @@ import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
 import { TaskCompletionBox } from "./TaskCompletedBox";
 import React = require("react");
 import { Transform } from "../../util/Transform";
+import { InkTool } from "../../../fields/InkField";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fc358f106..85130e69e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -757,13 +757,17 @@ export function createInkGroup(inksToGroup?: Doc[]) {
             ffView.props.removeDocument?.(selected);
             // TODO: nda - this is the code to actually get a new grouped collection
             const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+            if (newCollection) {
+                newCollection.height = newCollection[HeightSym]();
+                newCollection.width = newCollection[WidthSym]();
+            }
 
             // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
             newCollection && ffView.props.addDocument?.(newCollection);
             // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
 
-            InkTranscription.Instance.transcribeInk(newCollection, selected, false);
+            InkTranscription.Instance.transcribeInk(newCollection, selected, false, ffView);
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
@@ -778,56 +782,6 @@ export function createInkGroup(inksToGroup?: Doc[]) {
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
     createInkGroup();
-    // if (CurrentUserUtils.SelectedTool === InkTool.Write) {
-    //     CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-    //         const selected = ffView.unprocessedDocs;
-    //         // loop through selected an get the bound
-    //         const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-    //         selected.map(action(d => {
-    //             const x = NumCast(d.x);
-    //             const y = NumCast(d.y);
-    //             const width = d[WidthSym]();
-    //             const height = d[HeightSym]();
-    //             bounds.push({x, y, width, height});
-    //         }))
-
-    //         const aggregBounds = aggregateBounds(bounds, 0, 0);
-    //         const marqViewRef = ffView._marqueeViewRef.current;
-
-    //         // set the vals for bounds in marqueeView
-    //         if (marqViewRef) {
-    //             marqViewRef._downX = aggregBounds.x;
-    //             marqViewRef._downY = aggregBounds.y;
-    //             marqViewRef._lastX = aggregBounds.r;
-    //             marqViewRef._lastY = aggregBounds.b;
-    //         }        
-
-    //         selected.map(action(d => {
-    //             const dx = NumCast(d.x);
-    //             const dy = NumCast(d.y);
-    //             delete d.x;
-    //             delete d.y;
-    //             delete d.activeFrame;
-    //             delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-    //             delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-    //             // calculate pos based on bounds
-    //             if (marqViewRef?.Bounds) {
-    //                 d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-    //                 d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-    //             }
-    //             return d;
-    //         }));
-    //         ffView.props.removeDocument?.(selected);
-    //         // TODO: nda - this is the code to actually get a new grouped collection
-    //         const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
-
-    //         // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-    //         newCollection && ffView.props.addDocument?.(newCollection);
-    //         ffView.unprocessedDocs = [];
-    //     });
-    // }
-    // CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
 
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
-- 
cgit v1.2.3-70-g09d2


From 8147fd7d80746114a2245237e4dab2c89e2d49a2 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Mon, 2 May 2022 16:27:49 -0400
Subject: working on getting subgroups to work

---
 src/client/views/InkTranscription.tsx         | 87 ++++++++++++++++++++++++---
 src/client/views/nodes/button/FontIconBox.tsx |  4 +-
 2 files changed, 82 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index b1f807bdf..f7260c5b4 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -1,13 +1,14 @@
 import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
-import { DataSym, Doc, DocListCast } from '../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
 import { InkData, InkField } from "../../fields/InkField";
 import { Cast, DateCast, NumCast } from '../../fields/Types';
 import { DocumentType } from "../documents/DocumentTypes";
 import './InkTranscription.scss';
 import { aggregateBounds, Utils } from '../../Utils';
 import { timesSeries } from 'async';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 
 export class InkTranscription extends React.Component {
     static Instance: InkTranscription;
@@ -106,9 +107,12 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean) => {
+    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
         if (!groupDoc) return;
         this.inkDocs = inkDocs;
+        if (ffView) {
+            this.ffView = ffView;
+        }
 
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
@@ -165,6 +169,69 @@ export class InkTranscription extends React.Component {
         return max - min;
     }
 
+    subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
+         // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+        // loop through the words in wordInkDocMap
+        // for each word, get the inkDocs
+
+        // iterate through the keys of wordInkDocMap
+        wordInkDocMap.forEach((inkDocs: Doc[], word: string) => {
+            const selected = wordInkDocMap.get(word);
+            if (!selected) {
+                return;
+            }
+             // loop through selected an get the bound
+            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+            selected.map(action(d => {
+                const x = NumCast(d.x);
+                const y = NumCast(d.y);
+                const width = d[WidthSym]();
+                const height = d[HeightSym]();
+                bounds.push({ x, y, width, height });
+            }))
+
+            const aggregBounds = aggregateBounds(bounds, 0, 0);
+            const marqViewRef = this.ffView._marqueeViewRef.current;
+
+            // set the vals for bounds in marqueeView
+            if (marqViewRef) {
+                marqViewRef._downX = aggregBounds.x;
+                marqViewRef._downY = aggregBounds.y;
+                marqViewRef._lastX = aggregBounds.r;
+                marqViewRef._lastY = aggregBounds.b;
+            }
+
+            selected.map(action(d => {
+                const dx = NumCast(d.x);
+                const dy = NumCast(d.y);
+                delete d.x;
+                delete d.y;
+                delete d.activeFrame;
+                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                // calculate pos based on bounds
+                if (marqViewRef?.Bounds) {
+                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+                }
+                return d;
+            }));
+            this.ffView.props.removeDocument?.(selected);
+            // TODO: nda - this is the code to actually get a new grouped collection
+            const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+            if (newCollection) {
+                newCollection.height = newCollection[HeightSym]();
+                newCollection.width = newCollection[WidthSym]();
+                newCollection.title = word;
+            }
+
+
+            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+            newCollection && this.ffView.props.addDocument?.(newCollection);
+        });
+    }
+
     exportInk = (e: any, ref: any) => {
         const exports = e.detail.exports;
         console.log(e);
@@ -186,10 +253,12 @@ export class InkTranscription extends React.Component {
                     // map timestamp to strokes
                     const timestampWord = new Map<number, string>();
                     this.lastJiix.words.map((word: any) => {
-                        word.items.map((i: any) => {
-                            const ms = Date.parse(i.timestamp);
-                            timestampWord.set(ms, word.label);
-                        })
+                        if (word.items) {
+                            word.items.forEach((i: {id: string, timestamp: string, X: Array<number>, Y: Array<number>, F: Array<number>}) => {
+                                const ms = Date.parse(i.timestamp);
+                                timestampWord.set(ms, word.label);
+                            })
+                        }
                     })
 
                     const wordInkDocMap = new Map<string, Doc[]>();
@@ -210,8 +279,12 @@ export class InkTranscription extends React.Component {
                                 const newEntry = [inkDoc];
                                 wordInkDocMap.set(word, newEntry);
                             }
-                            
                         });
+                        // do the fun marq stuff
+                        // generate subgroups
+
+                        console.log(wordInkDocMap);
+                        this.subgroupsTranscriptions(wordInkDocMap);
 
                     }
                     // we can iterate through docs and for each doc index into the map by timestamp
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1c2a22226..d87b23d86 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -711,7 +711,7 @@ export function checkInksToGroup() {
     }
 }
 
-export function createInkGroup(inksToGroup?: Doc[]) {
+export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
@@ -767,7 +767,7 @@ export function createInkGroup(inksToGroup?: Doc[]) {
             // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
 
-            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false);
+            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
-- 
cgit v1.2.3-70-g09d2


From a2df645d2ffe83be24c20997c26eaf7257e847c0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 May 2022 18:09:22 -0400
Subject: fixed scaling  ink label in lightbox,etc views. changed ink strokes
 so that clicking on label of closed stroke is lower preceden than clicking on
 line.  fixed fontsize UI for setting ink label sizes.  can no longer slide
 ink pts that are cusps along curve.

---
 src/client/views/InkStrokeProperties.ts            | 61 +++++++++++-----------
 src/client/views/InkingStroke.tsx                  | 44 +++++++++++-----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/ImageBox.tsx                |  3 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  4 +-
 src/client/views/nodes/VideoBox.tsx                |  3 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 19 ++++---
 src/client/views/pdf/PDFViewer.tsx                 |  3 +-
 9 files changed, 84 insertions(+), 57 deletions(-)

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

diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index e4486fc78..922f309d7 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -230,42 +230,41 @@ export class InkStrokeProperties {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const order = controlIndex % 4;
             const closed = InkingStroke.IsClosed(ink);
-            if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1) {
+            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec("number"), []);
+            if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
                 const cpt_before = ink[controlIndex];
                 const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY };
-                if (true) {
-                    const newink = origInk.slice();
-                    const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
-                    const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
-                    const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt);
-                    const samplesLeft: Point[] = [];
-                    const samplesRight: Point[] = [];
-                    var startDir = { x: 0, y: 0 };
-                    var endDir = { x: 0, y: 0 };
-                    for (var i = 0; i < nearestSeg / 4 + 1; i++) {
-                        const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
-                        if (i === 0) startDir = bez.derivative(0);
-                        if (i === nearestSeg / 4) endDir = bez.derivative(nearestT);
-                        for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + .05 : 1); t += 0.05) {
-                            const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t));
-                            samplesLeft.push(new Point(pt.x, pt.y));
-                        }
+                const newink = origInk.slice();
+                const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
+                const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
+                const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt);
+                const samplesLeft: Point[] = [];
+                const samplesRight: Point[] = [];
+                var startDir = { x: 0, y: 0 };
+                var endDir = { x: 0, y: 0 };
+                for (var i = 0; i < nearestSeg / 4 + 1; i++) {
+                    const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
+                    if (i === 0) startDir = bez.derivative(0);
+                    if (i === nearestSeg / 4) endDir = bez.derivative(nearestT);
+                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + .05 : 1); t += 0.05) {
+                        const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t));
+                        samplesLeft.push(new Point(pt.x, pt.y));
                     }
-                    var { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
-                    for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) {
-                        const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
-                        if (i === nearestSeg / 4) startDir = bez.derivative(nearestT);
-                        if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                        for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + .05 + 1e-7 : 1 + 1e-7); t += 0.05) {
-                            const pt = bez.compute(Math.min(1, t));
-                            samplesRight.push(new Point(pt.x, pt.y));
-                        }
+                }
+                var { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) {
+                    const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
+                    if (i === nearestSeg / 4) startDir = bez.derivative(nearestT);
+                    if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
+                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + .05 + 1e-7 : 1 + 1e-7); t += 0.05) {
+                        const pt = bez.compute(Math.min(1, t));
+                        samplesRight.push(new Point(pt.x, pt.y));
                     }
-                    const { finalCtrls: rightCtrls, error: errorRight } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
-                    finalCtrls = finalCtrls.concat(rightCtrls);
-                    newink.splice(this._currentPoint - 4, 8, ...finalCtrls);
-                    return newink;
                 }
+                const { finalCtrls: rightCtrls, error: errorRight } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                finalCtrls = finalCtrls.concat(rightCtrls);
+                newink.splice(this._currentPoint - 4, 8, ...finalCtrls);
+                return newink;
             }
 
             return ink.map((pt, i) => {
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 06671961d..1871ab5a4 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -328,12 +328,25 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== "transparent" ? StrCast(this.layoutDoc.color, "transparent") : "transparent") :
             ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
         // Invisible polygonal line that enables the ink to be selected by the user.
-        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void) => InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
+        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) => InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
             inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
             StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" ? "none" : fillColor, startMarker, endMarker,
+            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker,
             markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0,
             false, downHdlr);
+        const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", ""));
+        const lineHeightGuess = (+getComputedStyle(document.body).lineHeight.replace("px", "")) / (+getComputedStyle(document.body).fontSize.replace("px", ""));
+        const interactions = {
+            onPointerLeave: action(() => this._nearestScrPt = undefined),
+            onPointerMove: this.props.isSelected() ? this.onPointerMove : undefined,
+            onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
+            onContextMenu: () => {
+                const cm = ContextMenu.Instance;
+                !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
+                cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
+                cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" });
+            }
+        };
         return <div className="inkStroke-wrapper">
             <svg className="inkStroke"
                 style={{
@@ -341,24 +354,19 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
                     cursor: this.props.isSelected() ? "default" : undefined
                 }}
-                onPointerLeave={action(e => this._nearestScrPt = undefined)}
-                onPointerMove={this.props.isSelected() ? this.onPointerMove : undefined}
-                onClick={e => this._handledClick && e.stopPropagation()}
-                onContextMenu={() => {
-                    const cm = ContextMenu.Instance;
-                    !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
-                    cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
-                    cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" });
-                }}
+                {...(!closed ? interactions : {})}
             >
-                {clickableLine(this.onPointerDown)}
                 {inkLine}
+                {clickableLine(this.onPointerDown)}
             </svg>
             {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? (null) :
                 <div className="inkStroke-text" style={{
                     color: StrCast(this.layoutDoc.textColor, "black"),
                     pointerEvents: this.props.isDocumentActive?.() ? "all" : undefined,
                     width: this.layoutDoc[WidthSym](),
+                    transform: `scale(${this.props.scaling?.() || 1})`,
+                    transformOrigin: "top left",
+                    top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.scaling?.() || 1)) / 2
                 }}>
                     <FormattedTextBox
                         {...OmitKeys(this.props, ['children']).omit}
@@ -366,7 +374,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                         yPadding={10}
                         xPadding={10}
                         fieldKey={"text"}
-                        fontSize={12}
+                        fontSize={fsize}
                         dontRegisterView={true}
                         noSidebar={true}
                         dontScale={true}
@@ -374,6 +382,16 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     />
                 </div>
             }
+            {!closed ? null : <svg className="inkStroke"
+                style={{
+                    transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
+                    cursor: this.props.isSelected() ? "default" : undefined, position: "absolute"
+                }}
+                {...interactions}
+            >
+                {clickableLine(this.onPointerDown, true)}
+            </svg>}
         </div>;
     }
 }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3f16d3c49..edadd59c0 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -38,7 +38,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @observable _contentView: DocumentView | undefined | null;
     get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
     get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
-    get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; }
+    get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; }
     get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); }
     get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); }
     get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d061cdd0b..e7c43e5cd 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -369,6 +369,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._marqueeing = undefined;
         this.props.select(false);
     }
+    savedAnnotations = () => this._savedAnnotations;
 
     render() {
         TraceMobx();
@@ -408,7 +409,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     docView={this.props.docViewPath().slice(-1)[0]}
                     addDocument={this.addDocument}
                     finishMarquee={this.finishMarquee}
-                    savedAnnotations={this._savedAnnotations}
+                    savedAnnotations={this.savedAnnotations}
                     annotationLayer={this._annotationLayer.current}
                     mainCont={this._mainCont.current}
                     anchorMenuCrop={this.crop}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 59e39e051..e7fc38ecd 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -537,7 +537,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     infoWidth = () => this.props.PanelWidth() / 5;
     infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-
+    savedAnnotations = () => this._savedAnnotations;
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => (null);
         // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
@@ -618,7 +618,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         addDocument={this.addDocumentWrapper}
                         docView={this.props.docViewPath().lastElement()}
                         finishMarquee={this.finishMarquee}
-                        savedAnnotations={this._savedAnnotations}
+                        savedAnnotations={this.savedAnnotations}
                         annotationLayer={this._annotationLayer.current}
                         mainCont={this._mainCont.current} />}
             </div>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e57cb1abe..c8b72990f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -800,6 +800,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
     }
 
+    savedAnnotations = () => this._savedAnnotations;
     render() {
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
         const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
@@ -847,7 +848,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         containerOffset={this.marqueeOffset}
                         addDocument={this.addDocWithTimecode}
                         finishMarquee={this.finishMarquee}
-                        savedAnnotations={this._savedAnnotations}
+                        savedAnnotations={this.savedAnnotations}
                         annotationLayer={this._annotationLayer.current}
                         mainCont={this._mainCont.current}
                     />}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index ca13590de..d3ff2586b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -656,7 +656,7 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
 ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
+        return RichTextMenu.Instance.fontSize.replace("px", "");
     }
     if (typeof size === "number") size = size.toString();
     if (size && Number(size).toString() === size) size += "px";
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4814d6e9a..c5f76cc8d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -9,7 +9,7 @@ import { wrapInList } from "prosemirror-schema-list";
 import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../../fields/Doc";
-import { Cast, StrCast } from "../../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { DocServer } from "../../../DocServer";
 import { LinkManager } from "../../../util/LinkManager";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -118,7 +118,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this.activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
         this._activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
-        this._activeFontSize = !activeSizes.length ? "13px" : activeSizes[0];
+        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, "10px")) : activeSizes[0];
         this._activeFontColor = !activeColors.length ? "black" : activeColors.length > 0 ? String(activeColors[0]) : "...";
         this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length > 0 ? String(activeHighlights[0]) : "...";
 
@@ -310,10 +310,17 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
     setFontSize = (fontSize: string) => {
         if (this.view) {
-            const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize });
-            this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
-            this.view.focus();
-            this.updateMenu(this.view, undefined, this.props);
+            if (this.view.state.selection.from === 1 && this.view.state.selection.empty &&
+                (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
+                this.TextView.dataDoc.fontSize = fontSize;
+                this.view.focus();
+                this.updateMenu(this.view, undefined, this.props);
+            } else {
+                const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize });
+                this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
+                this.view.focus();
+                this.updateMenu(this.view, undefined, this.props);
+            }
         }
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b55b2cf44..d3876906e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -562,6 +562,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         </>;
     }
     contentZoom = () => this._zoomed;
+    savedAnnotations = () => this._savedAnnotations;
     render() {
         TraceMobx();
         return <div className="pdfViewer-content">
@@ -586,7 +587,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                         addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
                         docView={this.props.docViewPath().lastElement()}
                         finishMarquee={this.finishMarquee}
-                        savedAnnotations={this._savedAnnotations}
+                        savedAnnotations={this.savedAnnotations}
                         annotationLayer={this._annotationLayer.current}
                         mainCont={this._mainCont.current} />}
             </div>
-- 
cgit v1.2.3-70-g09d2


From 5dd7905530dbfbac3318e55fc625fe46427e23c4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 13:45:21 -0400
Subject: adjusted placement of fonticonbox labels

---
 src/client/util/CurrentUserUtils.ts            | 64 ++++++++++++--------------
 src/client/views/MainView.tsx                  |  2 +-
 src/client/views/nodes/button/FontIconBox.scss |  8 ++--
 src/client/views/nodes/button/FontIconBox.tsx  | 23 +++++----
 4 files changed, 51 insertions(+), 46 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e0580d3c4..b9d2d63ba 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -181,21 +181,22 @@ export class CurrentUserUtils {
             "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,
             _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra
         });
-        const newIconsList = [
-            makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
-            makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
-            makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
-            makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
-            makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
-            makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
-            makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })),
-            // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
-        ].filter(d => d).map(d => d!);
         if (!templateIconsDoc) {
+            const newIconsList = [
+                makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
+                makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
+                makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
+                makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
+                makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
+                makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
+                makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+                makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+                makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })),
+                // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
+            ].filter(d => d).map(d => d!);
+
             doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true });
-            newIconsList.map(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d));
+            newIconsList.forEach(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d));
         }
     }
 
@@ -224,7 +225,6 @@ export class CurrentUserUtils {
                 backgroundColor: "white", system: true, cloneFieldFilter: new List<string>(["system"])
             });
             Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
-            FormattedTextBox.SelectOnLoad = textDoc[Id];
             doc.emptySlide = textDoc;
         }
         if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) {
@@ -287,11 +287,7 @@ export class CurrentUserUtils {
             ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", {
-                _width: 200, title: "text note", _autoHeight: true, system: true,
-                _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
-                cloneFieldFilter: new List<string>(["system"])
-            });
+            doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyImage === undefined) {
@@ -790,20 +786,20 @@ export class CurrentUserUtils {
                         "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
                     script: 'setFont(value, _readOnly_)'
                 },
-                { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
-                { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
+                { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
+                { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
                 { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
                 { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
-                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
-                { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
-                { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
+                { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
+                { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
+                { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
 
                 // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
                 // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
                 // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
-                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
-                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
+                { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
+                { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
+                { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
             ];
         return tools;
     }
@@ -816,9 +812,9 @@ export class CurrentUserUtils {
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
             { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
-            { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
-            { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
-            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
+            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
+            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
+            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
         ];
         return tools;
     }
@@ -862,19 +858,19 @@ export class CurrentUserUtils {
                 script: 'setView(value, _readOnly_)',
             }, // Always show
             {
-                title: "back", toolTip: "prev", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left",
+                title: "Back", toolTip: "Prev AnimationFrame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left",
                 hidden: 'IsNoviceMode()'
             },
             {
-                title: "Forward", toolTip: "next", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right",
+                title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right",
                 hidden: 'IsNoviceMode()'
             },
             {
-                title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
+                title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
                 script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
             {
-                title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
+                title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
                 script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
             }, // Only when a document is selected
             { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9be00fa9e..45dd84e31 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -173,7 +173,7 @@ export class MainView extends React.Component {
         fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
         fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
         fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
         fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index e3e2be515..df9046f12 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -64,7 +64,7 @@
         justify-items: center;
 
         &:hover {
-            filter:brightness(0.85) !important;
+            filter: brightness(0.85) !important;
         }
     }
 
@@ -166,8 +166,8 @@
             width: 45% !important;
             height: 45%;
         }
-        
-        &:hover{
+
+        &:hover {
             filter: brightness(0.85);
         }
     }
@@ -278,10 +278,12 @@
                 background-color: #e3e3e3;
                 box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
                 border-radius: $standard-border-radius;
+
                 input[type=range]::-webkit-slider-runnable-track {
                     background: gray;
                     height: 3px;
                 }
+
                 input[type=range]::-webkit-slider-thumb {
                     box-shadow: 1px 1px 1px #000000;
                     border: 1px solid #000000;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d3ff2586b..20b2be216 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -107,6 +107,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         // Script for checking the outcome of the toggle
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
+        const label = !Doc.UserDoc()._showLabel ? (null) :
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, position: "absolute" }}>
+                {this.label}
+            </div>;
+
         if (numBtnType === NumButtonType.Slider) {
             const dropdown =
                 <div
@@ -126,6 +131,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
                 >
                     {checkResult}
+                    {label}
                     {this.rootDoc.dropDownOpen ? dropdown : null}
                 </div>
             );
@@ -206,7 +212,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -277,7 +283,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         });
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -331,7 +337,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -375,7 +381,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Button label
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -387,7 +393,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                         <input type="checkbox"
                             checked={backgroundColor === Colors.MEDIUM_BLUE}
                         />
-                        <span className="slider round"></span>
+                        <span className="slider round" />
                     </label>
                 </div>
             );
@@ -416,7 +422,8 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                    {!this.label || !Doc.UserDoc()._showLabel ? (null) :
+                        <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
         );
@@ -443,12 +450,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
 
-- 
cgit v1.2.3-70-g09d2


From 366cd5f3799a78c297c9a0dc31eb0bd8c0e4c4bd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 16:35:24 -0400
Subject: some cleanup of currentUserUtils.  css fixes for fonticon boxes with
 labels

---
 src/client/util/CurrentUserUtils.ts                | 288 +++++++--------------
 .../collectionLinear/CollectionLinearView.tsx      |   2 +-
 src/client/views/nodes/button/FontIconBox.scss     |  44 +++-
 src/client/views/nodes/button/FontIconBox.tsx      |  44 ++--
 4 files changed, 146 insertions(+), 232 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b9d2d63ba..b9789ab7f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -6,7 +6,6 @@ import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 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";
@@ -23,7 +22,6 @@ import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
-import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
 import { DragManager } from "./DragManager";
@@ -53,6 +51,7 @@ interface Button {
     list?: string[];
     ignoreClick?: boolean;
     buttonText?: string;
+    hidden?: string;
 }
 
 export let resolvedPorts: { server: number, socket: number };
@@ -62,7 +61,6 @@ export class CurrentUserUtils {
     //TODO tfs: these should be temporary...
     private static mainDocId: string | undefined;
 
-    public static searchBtn: Doc;
     public static get id() { return this.curr_id; }
     public static get MainDocId() { return this.mainDocId; }
     public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
@@ -196,7 +194,6 @@ export class CurrentUserUtils {
             ].filter(d => d).map(d => d!);
 
             doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true });
-            newIconsList.forEach(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d));
         }
     }
 
@@ -329,15 +326,11 @@ export class CurrentUserUtils {
     }
 
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-    static async setupCreatorButtons(doc: Doc) {
+    static setupCreatorButtons(doc: Doc) {
         let alreadyCreatedButtons: string[] = [];
         const dragCreatorSet = Cast(doc.myItemCreators, Doc, null);
         if (dragCreatorSet) {
-            const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc));
-            if (dragCreators) {
-                const dragDocs = await Promise.all(Array.from(dragCreators));
-                alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
-            }
+            alreadyCreatedButtons = DocListCast(dragCreatorSet.data).map(d => StrCast(d.title));
         }
         const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
         const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({
@@ -373,7 +366,7 @@ export class CurrentUserUtils {
         return doc.myItemCreators as Doc;
     }
 
-    static async menuBtnDescriptions(doc: Doc) {
+    static menuBtnDescriptions(doc: Doc) {
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
@@ -390,7 +383,7 @@ export class CurrentUserUtils {
     static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (doc.menuStack === undefined) {
             await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
+            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
                 Docs.Create.FontIconDocument({
                     icon,
                     btnType: ButtonType.MenuButton,
@@ -411,17 +404,7 @@ export class CurrentUserUtils {
                 })
             );
 
-            menuBtns.forEach(menuBtn => {
-                if (menuBtn.title === "Search") {
-                    this.searchBtn = menuBtn;
-                }
-            });
-
-            menuBtns.forEach(menuBtn => {
-                if (menuBtn.title === "Search") {
-                    doc.searchBtn = menuBtn;
-                }
-            });
+            doc.searchBtn = menuBtns.find(btn => btn.title === "Search");
 
             doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
                 title: "menuItemPanel",
@@ -436,19 +419,6 @@ export class CurrentUserUtils {
                 _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
             }));
         }
-        // this resets all sidebar buttons to being deactivated
-        PromiseValue(Cast(doc.menuStack, Doc)).then(stack => {
-            stack && PromiseValue(stack.data).then(btns => {
-                DocListCastAsync(btns).then(bts => bts?.forEach(btn => {
-                    btn.dontUndo = true;
-                    btn.system = true;
-                    if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files
-                        btn.target = Doc.UserDoc().myFilesystem;
-                        btn.title = "My Files";
-                    }
-                }));
-            });
-        });
         return doc.menuStack as Doc;
     }
 
@@ -578,35 +548,26 @@ export class CurrentUserUtils {
 
     // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.
     // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
-    static async setupToolsBtnPanel(doc: Doc) {
+    static setupToolsBtnPanel(doc: Doc) {
         // setup a masonry view of all he creators
-        const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
+        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
 
-        doc["tabs-button-tools"] = undefined;
-
-        if (doc.myCreators === undefined) {
-            doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
-                title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true,
-                _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true,
-            }));
-        }
-        // setup a color picker
-        if (doc.myColorPicker === undefined) {
-            const color = Docs.Create.ColorDocument({
-                title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true
-            });
-            doc.myColorPicker = new PrefetchProxy(color);
-        }
+        doc.myCreators = doc.myCreators ?? new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
+            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true,
+            _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true,
+        }));
+        if (!DocListCast(doc.myCreators).includes(creatorBtns as Doc) || !DocListCast(doc.myCreators).includes(templateBtns as Doc)) Doc.GetProto(doc.myCreators as Doc).data = new List<Doc>([creatorBtns, templateBtns]);
 
-        if (doc.myTools === undefined) {
-            const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], {
-                title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-                system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0",
-            })) as any as Doc;
+        doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({
+            title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true
+        }));
 
-            doc.myTools = toolsStack;
-        }
+        doc.myTools = doc.myTools ?? new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], {
+            title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+            system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0",
+        })) as any as Doc;
+        if (!DocListCast(doc.myTools).includes(doc.myCreators as Doc)) Doc.GetProto(doc.myTools as Doc).data = new List<Doc>([doc.myCreators as Doc]);
     }
 
     static async setupDashboards(doc: Doc) {
@@ -639,8 +600,7 @@ export class CurrentUserUtils {
         return doc.myDashboards as any as Doc;
     }
 
-    static async setupPresentations(doc: Doc) {
-        await doc.myTrails;
+    static setupPresentations(doc: Doc) {
         if (doc.myTrails === undefined) {
             const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
             const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
@@ -743,7 +703,7 @@ export class CurrentUserUtils {
     // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
     static async setupSidebarButtons(doc: Doc) {
         CurrentUserUtils.setupSidebarContainer(doc);
-        await CurrentUserUtils.setupToolsBtnPanel(doc);
+        CurrentUserUtils.setupToolsBtnPanel(doc);
         CurrentUserUtils.setupImportSidebar(doc);
         CurrentUserUtils.setupDashboards(doc);
         CurrentUserUtils.setupPresentations(doc);
@@ -764,17 +724,26 @@ export class CurrentUserUtils {
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupDockedButtons(doc: Doc) {
-        if (doc["dockedBtn-undo"] === undefined) {
-            doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("undo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true });
-        }
-        if (doc["dockedBtn-redo"] === undefined) {
-            doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("redo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true });
-        }
         if (doc.dockedBtns === undefined) {
-            doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
+            if (doc["dockedBtn-undo"] === undefined) {
+                doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({
+                    title: "undo", icon: "undo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true,
+                    onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ToolButton, _dropAction: "alias",
+                    _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo"
+                });
+            }
+            if (doc["dockedBtn-redo"] === undefined) {
+                doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({
+                    title: "redo", icon: "redo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true,
+                    onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ToolButton, _dropAction: "alias",
+                    _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo",
+                });
+            }
+            doc.dockedBtns = CurrentUserUtils.linearButtonList({
+                title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
+                linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+            }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
         }
-        (doc["dockedBtn-undo"] as Doc).dontUndo = true;
-        (doc["dockedBtn-redo"] as Doc).dontUndo = true;
     }
 
     static textTools(doc: Doc) {
@@ -821,16 +790,7 @@ export class CurrentUserUtils {
 
     static schemaTools(doc: Doc) {
         const tools: Button[] =
-            [
-                {
-                    title: "Show preview",
-                    toolTip: "Show preview of selected document",
-                    btnType: ButtonType.ToggleButton,
-                    buttonText: "Show Preview",
-                    icon: "eye",
-                    click: 'toggleSchemaPreview(_readOnly_)',
-                },
-            ];
+            [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview(_readOnly_)', }];
         return tools;
     }
 
@@ -846,7 +806,7 @@ export class CurrentUserUtils {
         return tools;
     }
 
-    static async contextMenuTools(doc: Doc) {
+    static contextMenuTools(doc: Doc) {
         return [
             {
                 title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
@@ -857,128 +817,52 @@ export class CurrentUserUtils {
                 CollectionViewType.Grid],
                 script: 'setView(value, _readOnly_)',
             }, // Always show
-            {
-                title: "Back", toolTip: "Prev AnimationFrame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left",
-                hidden: 'IsNoviceMode()'
-            },
-            {
-                title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right",
-                hidden: 'IsNoviceMode()'
-            },
-            {
-                title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
-                script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
-            }, // Only when a document is selected
-            {
-                title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
-                script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
-            }, // Only when a document is selected
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame(_readOnly_)', icon: "chevron-left", hidden: 'IsNoviceMode()' },
+            { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame(_readOnly_)', icon: "chevron-right", hidden: 'IsNoviceMode()' },
+            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected
+            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', },
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
-            { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
-            { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
-            { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
-            { title: "Schema", type: "schemaTools", subMenu: true, hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
+            { title: "Text", type: "textTools", subMenu: CurrentUserUtils.textTools(doc), expanded: 'selectedDocumentType("rtf")' }, // Always available
+            { title: "Ink", type: "inkTools", subMenu: CurrentUserUtils.inkTools(doc), expanded: 'selectedDocumentType("ink")' }, // Always available
+            { title: "Web", type: "webTools", subMenu: CurrentUserUtils.webTools(doc), hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
+            { title: "Schema", type: "schemaTools", subMenu: CurrentUserUtils.schemaTools(doc), hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
         ];
     }
 
     // Sets up the default context menu buttons
-    static async setupContextMenuButtons(doc: Doc) {
+    static setupContextMenuButtons(doc: Doc) {
+        const btnFunc = (params: Button) => Docs.Create.FontIconDocument({
+            title: params.title, icon: params.icon, toolTip: params.toolTip, color: Colors.WHITE, system: true, dontUndo: true, ignoreClick: params.ignoreClick,
+            _nativeWidth: params.width ? params.width : 30,
+            _nativeHeight: 30,
+            _width: params.width ? params.width : 30,
+            _height: 30,
+            btnType: params.btnType,
+            numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,
+            btnList: new List<string>(params.list),
+            _stayInCollection: true,
+            _hideContextMenu: true,
+            _lockedPosition: true,
+            _dropAction: "alias",
+            _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+            script: params.script ? ScriptField.MakeScript(params.script, { value: "any" }) : undefined,
+            backgroundColor: params.click ? ComputedField.MakeFunction(params.click) as any : "transparent",
+            onClick: params.click ? ScriptField.MakeScript(params.click, { scriptContext: "any" }, { _readOnly_: false }) : undefined,
+            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
+        });
         if (doc.contextMenuBtns === undefined) {
-            const docList: Doc[] = [];
-
-            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, backgroundColor, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
-                const menuDocList: Doc[] = [];
-                if (subMenu) {
-                    // default is textTools
-                    let tools: Button[];
-                    switch (type) {
-                        case "inkTools":
-                            tools = CurrentUserUtils.inkTools(doc);
-                            break;
-                        case "schemaTools":
-                            tools = CurrentUserUtils.schemaTools(doc);
-                            break;
-                        case "webTools":
-                            tools = CurrentUserUtils.webTools(doc);
-                            break;
-                        case "textTools":
-                            tools = CurrentUserUtils.textTools(doc);
-                            break;
-                        default:
-                            tools = CurrentUserUtils.textTools(doc);
-                            break;
-                    }
-                    tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
-                        const backgroundColor = click ? ComputedField.MakeFunction(click) as any : "transparent";
-                        menuDocList.push(Docs.Create.FontIconDocument({
-                            _nativeWidth: width ? width : 25,
-                            _nativeHeight: 25,
-                            _width: width ? width : 25,
-                            _height: 25,
-                            icon,
-                            toolTip,
-                            numBtnType,
-                            numBtnMin,
-                            numBtnMax,
-                            script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
-                            btnType: btnType,
-                            btnList: new List<string>(list),
-                            ignoreClick: ignoreClick,
-                            _stayInCollection: true,
-                            _hideContextMenu: true,
-                            _lockedPosition: true,
-                            system: true,
-                            dontUndo: true,
-                            title,
-                            switchToggle,
-                            color: Colors.WHITE,
-                            backgroundColor,
-                            _dropAction: "alias",
-                            _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                            onClick: click ? ScriptField.MakeScript(click) : undefined
-                        }));
-                    });
-                    docList.push(CurrentUserUtils.linearButtonList({
-                        linearViewSubMenu: true,
-                        flexGap: 0,
-                        ignoreClick: true,
-                        linearViewExpandable: true,
-                        icon: title,
-                        _height: 30,
-                        // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, {  }, { _readOnly_: true }) as any : "transparent",
-                        linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined,
-                        hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
-                    }, menuDocList));
-                } else {
-                    docList.push(Docs.Create.FontIconDocument({
-                        _nativeWidth: width ? width : 25,
-                        _nativeHeight: 25,
-                        _width: width ? width : 25,
-                        _height: 25,
-                        icon,
-                        toolTip,
-                        script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
-                        btnType,
-                        btnList: new List<string>(list),
-                        ignoreClick,
-                        _stayInCollection: true,
-                        _hideContextMenu: true,
-                        _lockedPosition: true,
-                        system: true,
-                        dontUndo: true,
-                        title,
-                        color: Colors.WHITE,
-                        backgroundColor: backgroundColor ? ComputedField.MakeFunction(backgroundColor) as any : "transparent",
-                        _dropAction: "alias",
-                        hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
-                        _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                        onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }, { _readOnly_: false }) : undefined
-                    }));
-                }
-            });
-
-            doc.contextMenuBtns = CurrentUserUtils.linearButtonList({ title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, docList);
+            doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
+                { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
+                CurrentUserUtils.contextMenuTools(doc).map(params =>
+                    !params.subMenu ?
+                        btnFunc(params) :
+                        CurrentUserUtils.linearButtonList({
+                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
+                            linearViewExpandable: true, icon: params.title, _height: 30,
+                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
+                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
+                        }, params.subMenu.map(subParams => btnFunc(subParams)))));
         }
     }
 
@@ -1463,7 +1347,8 @@ ScriptingGlobals.add(function makeTopLevelFolder() {
 ScriptingGlobals.add(function toggleComicMode() {
     Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic";
 });
-ScriptingGlobals.add(function nextKeyFrame() {
+ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
+    if (readOnly) return;
     const sel = SelectionManager.Views()[0];
     const col = (sel.ComponentView as CollectionFreeFormView);
     const currentFrame = Cast(sel.props.Document._currentFrame, "number", null);
@@ -1476,7 +1361,8 @@ ScriptingGlobals.add(function nextKeyFrame() {
     sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame));
 }
 );
-ScriptingGlobals.add(function prevKeyFrame() {
+ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
+    if (readOnly) return;
     const sel = SelectionManager.Views()[0];
     const col = (sel.ComponentView as CollectionFreeFormView);
     const currentFrame = Cast(sel.props.Document._currentFrame, "number", null);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index bec582dcd..c0a33a5e0 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -151,7 +151,7 @@ export class CollectionLinearView extends CollectionSubView() {
                 removeDocument={this.props.removeDocument}
                 ScreenToLocalTransform={docXf}
                 PanelWidth={nested ? doc[WidthSym] : this.dimension}
-                PanelHeight={nested ? doc[HeightSym] : this.dimension}
+                PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                 renderDepth={this.props.renderDepth + 1}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index df9046f12..6cd56f84e 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -18,12 +18,12 @@
 
     .fontIconBox-label {
         color: $white;
-        position: relative;
+        bottom: 0;
+        position: absolute;
         text-align: center;
         font-size: 7px;
         letter-spacing: normal;
         background-color: inherit;
-        margin-top: 5px;
         border-radius: 8px;
         padding: 0;
         width: 100%;
@@ -40,8 +40,10 @@
         height: 80%;
     }
 
-    &.clickBtn {
+    &.clickBtn,
+    &.clickBtnLabel {
         cursor: pointer;
+        flex-direction: column;
 
         &:hover {
             background-color: rgba(0, 0, 0, 0.3) !important;
@@ -53,6 +55,12 @@
         }
     }
 
+    &.clickBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
     &.textBtn {
         display: grid;
         /* grid-row: auto; */
@@ -68,8 +76,10 @@
         }
     }
 
-    &.tglBtn {
+    &.tglBtn,
+    &.tglBtnLabel {
         cursor: pointer;
+        flex-direction: column;
 
         &.switch {
             //TOGGLE
@@ -146,10 +156,19 @@
         }
     }
 
-    &.toolBtn {
+    &.tglBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
+    &.toolBtn,
+    &.toolBtnLabel {
         cursor: pointer;
         width: 100%;
         border-radius: 100%;
+        flex-direction: column;
+        margin-top: -4px;
 
         svg {
             width: 60% !important;
@@ -157,6 +176,12 @@
         }
     }
 
+    &.toolBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
     &.menuBtn {
         cursor: pointer !important;
         border-radius: 0px;
@@ -174,7 +199,8 @@
 
 
 
-    &.colorBtn {
+    &.colorBtn,
+    &.colorBtnLabel {
         color: black;
         cursor: pointer;
         flex-direction: column;
@@ -204,6 +230,12 @@
         }
     }
 
+    &.colorBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
     &.drpdownList {
         width: 100%;
         display: grid;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 20b2be216..e6d8fe88d 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -108,23 +108,19 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
         const label = !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, position: "absolute" }}>
+            <div className="fontIconBox-label">
                 {this.label}
             </div>;
 
         if (numBtnType === NumButtonType.Slider) {
-            const dropdown =
-                <div
-                    className="menuButton-dropdownBox"
-                    onPointerDown={e => e.stopPropagation()}
-                >
-                    <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult}
-                        className={"menu-slider"} id="slider"
-                        onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")}
-                        onPointerUp={() => this._batch?.end()}
-                        onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }}
-                    />
-                </div>;
+            const dropdown = <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} >
+                <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult}
+                    className={"menu-slider"} id="slider"
+                    onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")}
+                    onPointerUp={() => this._batch?.end()}
+                    onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }}
+                />
+            </div>;
             return (
                 <div
                     className={`menuButton ${this.type} ${numBtnType}`}
@@ -212,7 +208,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -283,7 +279,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         });
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: 0, position: "absolute", color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
 
@@ -337,7 +333,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
@@ -348,7 +344,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         </div>;
         setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
-            <div className={`menuButton ${this.type} ${this.colorPickerClosed}`}
+            <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
                 onPointerDown={e => e.stopPropagation()}>
@@ -381,7 +377,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Button label
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
@@ -399,7 +395,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             );
         } else {
             return (
-                <div className={`menuButton ${this.type}`}
+                <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`}
                     style={{ opacity: 1, backgroundColor, color }}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
@@ -423,7 +419,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
                     {!this.label || !Doc.UserDoc()._showLabel ? (null) :
-                        <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                        <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
         );
@@ -450,12 +446,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor: "transparent" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
 
@@ -496,7 +492,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ToolButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
+                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -508,7 +504,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
-- 
cgit v1.2.3-70-g09d2


From 53cae5e2ab9267295a824ff721c119ada5e5dc20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 31 May 2022 13:03:19 -0400
Subject: added a pop up menu for viewing dashField view collection.  added a
 menu button for turning off autoLinking to text selection.  added ability to
 add button to top contextMenu buttons without blowing away db.

---
 src/client/util/CurrentUserUtils.ts                | 34 +++++++++
 src/client/views/MainView.tsx                      |  2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  8 +++
 .../views/nodes/formattedText/DashFieldView.tsx    | 81 +++++++++++++++++-----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 12 ++--
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +
 .../views/nodes/formattedText/RichTextMenu.tsx     | 14 +++-
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  | 14 ++++
 10 files changed, 144 insertions(+), 27 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 083530ce7..c4e2afd08 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -745,6 +745,7 @@ export class CurrentUserUtils {
                 { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
                 { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
                 { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
+                { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' },
             ];
         return tools;
     }
@@ -834,11 +835,44 @@ export class CurrentUserUtils {
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
+                            title: params.title,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
                             hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
                         }, params.subMenu.map(btnFunc))));
+        } else {
+            const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data);
+            let prev = "";
+            CurrentUserUtils.contextMenuTools(doc).forEach(params => {
+                const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title);
+                if (!menuBtnDoc) {
+                    const newMenuBtnDoc = !params.subMenu ?
+                        btnFunc(params) :
+                        CurrentUserUtils.linearButtonList({
+                            title: params.title,
+                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
+                            linearViewExpandable: true, icon: params.title, _height: 30,
+                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
+                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
+                        }, params.subMenu.map(btnFunc));
+                    const after = menuBtnList.find(doc => doc.title === prev);
+                    Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after);
+                }
+                const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined;
+                if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) {
+                    let prevSub = "";
+                    params.subMenu.forEach(sub => {
+                        if (!subMenuBtnList.find(doc => doc.title === sub.title)) {
+                            const newSubMenuBtnDoc = btnFunc(sub);
+                            const after = subMenuBtnList.find(doc => doc.title === prevSub);
+                            Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
+                        }
+                        prevSub = params.title;
+                    })
+                }
+                prev = params.title;
+            });
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ff2857739..55190001b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -50,6 +50,7 @@ import { AudioBox } from './nodes/AudioBox';
 import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
+import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
@@ -686,6 +687,7 @@ export class MainView extends React.Component {
             <ContextMenu />
             <RadialMenu />
             <AnchorMenu />
+            <DashFieldViewMenu />
             <MarqueeOptionsMenu />
             <OverlayView />
             <TimelineMenu />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f4cb08f8f..259cc1ee5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1166,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== "Tab";
             const layoutKey = StrCast(docView.LayoutFieldKey);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index e6d8fe88d..625f5d14a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -666,6 +666,14 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
     if (editorView) RichTextMenu.Instance.setFontSize(size);
     else Doc.UserDoc()._fontSize = size;
 });
+ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    if (checkResult) {
+        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+    }
+    if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
+    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
+});
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 1a8352b72..6a3f9ed00 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -8,35 +8,41 @@ import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { Cast, StrCast } from "../../../../fields/Types";
 import { DocServer } from "../../../DocServer";
-import { DocUtils } from "../../../documents/Documents";
 import { CollectionViewType } from "../../collections/CollectionView";
 import "./DashFieldView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
+import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
+import { Tooltip } from "@material-ui/core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 export class DashFieldView {
     _fieldWrapper: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+        const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
+
         this._fieldWrapper = document.createElement("div");
         this._fieldWrapper.style.width = node.attrs.width;
         this._fieldWrapper.style.height = node.attrs.height;
         this._fieldWrapper.style.fontWeight = "bold";
         this._fieldWrapper.style.position = "relative";
         this._fieldWrapper.style.display = "inline-block";
+        this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal;
         this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
 
-        ReactDOM.render(<DashFieldViewInternal
+        setTimeout(() => ReactDOM.render(<DashFieldViewInternal
             fieldKey={node.attrs.fieldKey}
             docid={node.attrs.docid}
             width={node.attrs.width}
             height={node.attrs.height}
             hideKey={node.attrs.hideKey}
             tbox={tbox}
-        />, this._fieldWrapper);
+        />, this._fieldWrapper));
         (this as any).dom = this._fieldWrapper;
     }
     destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
@@ -76,16 +82,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._reactionDisposer?.();
     }
 
-    multiValueDelimeter = ";";
+    public static multiValueDelimeter = ";";
+    public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
+        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
+        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
+        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" }
+    }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         if (this._dashDoc) {
-            const dashVal = this._dashDoc[this._fieldKey] ?? this._dashDoc[DataSym][this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
-            const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
-            const boolVal = Cast(fval, "boolean", null);
-            const strVal = Field.toString(fval as Field) || "";
-
+            const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey);
             // field value is a boolean, so use a checkbox or similar widget to display it
             if (boolVal === true || boolVal === false) {
                 return <input
@@ -108,9 +115,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                     ref={r => {
                         r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
                         r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
-                        r?.addEventListener("pointerdown", action((e) => {
-                            e.stopPropagation();
-                        }));
+                        r?.addEventListener("pointerdown", action(e => e.stopPropagation()));
                     }} >
                     {strVal}
                 </span>;
@@ -161,7 +166,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                         if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
                         Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
                     } else {
-                        const splits = newText.split(this.multiValueDelimeter);
+                        const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
                         if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
                             const strVal = splits.length > 1 ? new List<string>(splits) : newText;
                             if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
@@ -173,11 +178,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
-
-    // clicking on the label creates a pivot view collection of all documents
-    // in the same collection.  The pivot field is the fieldKey of this label
-    onPointerDownLabelSpan = (e: any) => {
-        e.stopPropagation();
+    createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.ContainingCollectionView;
         while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
             container = container.props.ContainingCollectionView;
@@ -196,6 +197,16 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
+
+    // clicking on the label creates a pivot view collection of all documents
+    // in the same collection.  The pivot field is the fieldKey of this label
+    onPointerDownLabelSpan = (e: any) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => {
+            DashFieldViewMenu.createFieldView = this.createPivotForField;
+            DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
+        });
+    }
+
     render() {
         return <div className="dashFieldView" style={{
             width: this.props.width,
@@ -210,4 +221,38 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
         </div >;
     }
+}
+@observer
+export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
+    static Instance: DashFieldViewMenu;
+    static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
+    constructor(props: any) {
+        super(props);
+        DashFieldViewMenu.Instance = this;
+    }
+    @action
+    showFields = (e: React.MouseEvent) => {
+        DashFieldViewMenu.createFieldView(e);
+        DashFieldViewMenu.Instance.fadeOut(true);
+    }
+
+    public show = (x: number, y: number) => {
+        this.jumpTo(x, y, true);
+        const hideMenu = () => {
+            this.fadeOut(true);
+            document.removeEventListener("pointerdown", hideMenu);
+        }
+        document.addEventListener("pointerdown", hideMenu)
+    }
+    render() {
+        const buttons = [
+            <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+                <button className="antimodeMenu-button" onPointerDown={this.showFields}>
+                    <FontAwesomeIcon icon="eye" size="lg" />
+                </button>
+            </Tooltip>,
+        ];
+
+        return this.getElement(buttons);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d468822c0..ce82821b6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -391,16 +391,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             flattened1.forEach((flat, i) => {
                 const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm);
                 this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-                alink = alink ?? (DocListCast(this.Document.links).find(link =>
-                    Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
-                    Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
-                        LinkManager.AutoKeywords)!);
-                newAutoLinks.add(alink);
                 const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
                 const sel = flattened[i];
                 tr = tr.addMark(sel.from, sel.to, splitter);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-                    if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                    if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                        alink = alink ?? (DocListCast(this.Document.links).find(link =>
+                            Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
+                            Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
+                                LinkManager.AutoKeywords)!);
+                        newAutoLinks.add(alink);
                         const allAnchors = [{ href: Doc.localServerPath(target), title: "a link", anchorId: this.props.Document[Id] }];
                         allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
                         const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" });
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index e979ae59e..fb49b0698 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -82,6 +82,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true);
     bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Enter", () => props.onKey?.(event, props) ? true : true);
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index c5f76cc8d..3df1e45a5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -35,6 +35,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
     @observable private collapsed: boolean = false;
+    @observable private _noLinkActive: boolean = false;
     @observable private _boldActive: boolean = false;
     @observable private _italicsActive: boolean = false;
     @observable private _underlineActive: boolean = false;
@@ -79,6 +80,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this._reaction?.();
     }
 
+    @computed get noAutoLink() { return this._noLinkActive; }
     @computed get bold() { return this._boldActive; }
     @computed get underline() { return this._underlineActive; }
     @computed get italics() { return this._italicsActive; }
@@ -220,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         let activeMarks: MarkType[] = [];
         if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks;
 
-        const markGroup = [schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
+        const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
         if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
         //current selection
         const { empty, ranges, $to } = this.view.state.selection as TextSelection;
@@ -264,6 +266,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     setActiveMarkButtons(activeMarks: MarkType[] | undefined) {
         if (!activeMarks) return;
 
+        this._noLinkActive = false;
         this._boldActive = false;
         this._italicsActive = false;
         this._underlineActive = false;
@@ -273,6 +276,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         activeMarks.forEach(mark => {
             switch (mark.name) {
+                case "noAutoLinkAnchor": this._noLinkActive = true; break;
                 case "strong": this._boldActive = true; break;
                 case "em": this._italicsActive = true; break;
                 case "underline": this._underlineActive = true; break;
@@ -283,6 +287,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         });
     }
 
+    toggleNoAutoLinkAnchor = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.TextView.autoLink();
+            this.view.focus();
+        }
+    }
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 427e05edb..8851d52e4 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -348,7 +348,7 @@ export class RichTextRules {
                         this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
                     }
                     const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView);
+                    return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(" ");
                 }),
 
 
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 1f6ce014f..2fde5c7ba 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -48,6 +48,20 @@ export const marks: { [index: string]: MarkSpec } = {
             return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
         }
     },
+    noAutoLinkAnchor: {
+        attrs: {},
+        inclusive: false,
+        parseDOM: [{
+            tag: "div", getAttrs(dom: any) {
+                return {
+                    noAutoLink: dom.getAttribute("data-noAutoLink"),
+                };
+            }
+        }],
+        toDOM(node: any) {
+            return ["span", { "data-noAutoLink": "true" }, 0];
+        }
+    },
     // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text,
     // and a title for use in menus and hover. `title`
     // defaults to the empty string. Rendered and parsed as an `<a>`
-- 
cgit v1.2.3-70-g09d2


From ba7824a91da113cc813c58b678719092c96d79a2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 3 Jun 2022 16:02:24 -0400
Subject: fixed opening closed tabs from Files sidebar to re-use best Alias
 before making a new alias.

---
 src/.DS_Store                                      | Bin 8196 -> 8196 bytes
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/util/DocumentManager.ts                 |  28 ++++++++++-----------
 src/client/views/LightboxView.tsx                  |   5 ++--
 src/client/views/collections/TabDocView.tsx        |  15 ++++++++++-
 src/client/views/collections/TreeView.tsx          |   4 +--
 src/client/views/nodes/DocumentIcon.tsx            |   4 +--
 src/client/views/nodes/DocumentView.tsx            |   4 ---
 src/client/views/nodes/button/FontIconBox.tsx      |  11 ++++----
 .../views/nodes/formattedText/DashFieldView.tsx    |   6 ++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  14 ++---------
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 13 files changed, 48 insertions(+), 49 deletions(-)

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

diff --git a/src/.DS_Store b/src/.DS_Store
index b0987293b..4bf9cdac7 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2ed6c3cbe..85ad58726 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -869,7 +869,7 @@ export class CurrentUserUtils {
                             Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
                         }
                         prevSub = params.title;
-                    })
+                    });
                 }
                 prev = params.title;
             });
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index aeddc3249..a82f99d5a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -16,7 +16,7 @@ import { SelectionManager } from './SelectionManager';
 export class DocumentManager {
 
     //global holds all of the nodes (regardless of which collection they're in)
-    @observable public DocumentViews: DocumentView[] = [];
+    @observable public DocumentViews = new Set<DocumentView>();
     @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
     @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
@@ -46,7 +46,7 @@ export class DocumentManager {
             // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
             //     view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString));
         } else {
-            this.DocumentViews.push(view);
+            this.DocumentViews.add(view);
         }
     }
     public RemoveView = action((view: DocumentView) => {
@@ -61,8 +61,7 @@ export class DocumentManager {
             const index = this.LinkAnchorBoxViews.indexOf(view);
             this.LinkAnchorBoxViews.splice(index, 1);
         } else {
-            const index = this.DocumentViews.indexOf(view);
-            index !== -1 && this.DocumentViews.splice(index, 1);
+            this.DocumentViews.delete(view);
         }
         SelectionManager.DeselectView(view);
     });
@@ -70,13 +69,13 @@ export class DocumentManager {
     //gets all views
     public getDocumentViewsById(id: string) {
         const toReturn: DocumentView[] = [];
-        DocumentManager.Instance.DocumentViews.map(view => {
+        Array.from(DocumentManager.Instance.DocumentViews).map(view => {
             if (view.rootDoc[Id] === id) {
                 toReturn.push(view);
             }
         });
         if (toReturn.length === 0) {
-            DocumentManager.Instance.DocumentViews.map(view => {
+            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                 const doc = view.rootDoc.proto;
                 if (doc && doc[Id] && doc[Id] === id) {
                     toReturn.push(view);
@@ -96,14 +95,14 @@ export class DocumentManager {
         const passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
         for (const pass of passes) {
-            DocumentManager.Instance.DocumentViews.map(view => {
+            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                 if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
                     return;
                 }
             });
             if (!toReturn) {
-                DocumentManager.Instance.DocumentViews.map(view => {
+                Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                     const doc = view.rootDoc.proto;
                     if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                         toReturn = view;
@@ -122,9 +121,8 @@ export class DocumentManager {
     }
 
     public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
-        const docViews = DocumentManager.Instance.DocumentViews;
         const views: DocumentView[] = [];
-        docViews.map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
+        Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
     }
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
@@ -133,8 +131,8 @@ export class DocumentManager {
     }
     public getDocumentViews(toFind: Doc): DocumentView[] {
         const toReturn: DocumentView[] = [];
-        const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
-        const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
+        const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
+        const lightViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
 
         // heuristic to return the "best" documents first:
         //   choose a document in the lightbox first
@@ -290,7 +288,7 @@ export class DocumentManager {
         }
     }
 }
-export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) {
+export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
     const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView);
     if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) {
@@ -300,7 +298,9 @@ export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) {
     else {
         const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null);
         const showDoc = context || doc;
-        CollectionDockingView.AddSplit(showDoc === Doc.GetProto(showDoc) ? Doc.MakeAlias(showDoc) : showDoc, "right") && context &&
+        const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
+
+        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context &&
             setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
     }
 }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ddfc8b249..3d2769f66 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -275,9 +275,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 <div className="lightboxView-tabBtn" title={"open in tab"}
                     onClick={e => {
                         e.stopPropagation();
-                        // CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
-                        LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
-                        console.log("lightbox to tab triggered")
+                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
+                        //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8d84e9a56..d64cb2fd7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -66,6 +66,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     get stack() { return (this.props as any).glContainer.parent.parent; }
     get tab() { return (this.props as any).glContainer.tab; }
     get view() { return this._view; }
+    _lastTab: any;
+    _lastView: DocumentView | undefined;
 
     @action
     init = (tab: any, doc: Opt<Doc>) => {
@@ -266,6 +268,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
     componentWillUnmount() {
         this._tabReaction?.();
+        this._view && DocumentManager.Instance.RemoveView(this._view);
         this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
@@ -353,7 +356,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
     @computed get docView() {
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
-            <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
+            <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => {
+                this._lastView && DocumentManager.Instance.RemoveView(this._lastView);
+                this._view = r;
+                this._lastView = this._view;
+            })}
                 renderDepth={0}
                 Document={this._document}
                 DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
@@ -409,6 +416,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 height: "100%", width: "100%"
             }} ref={ref => {
                 if (this._mainCont = ref) {
+                    if (this._lastTab) {
+                        console.log("DUP tab")
+                        this._view && DocumentManager.Instance.RemoveView(this._view);
+                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
+                    }
+                    this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
                     DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
                     new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 661db7997..9de44fb00 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -183,7 +183,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
             const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
             this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox");
         }
@@ -609,7 +609,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     // TODO: currently doc focus works, but can't seem to edit title
     // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.())
+        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
     }
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index a9c998757..56de2d1fc 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -52,7 +52,7 @@ export class DocumentIconContainer extends React.Component {
                 };
             },
             getVars() {
-                const docs = DocumentManager.Instance.DocumentViews;
+                const docs = Array.from(DocumentManager.Instance.DocumentViews);
                 const capturedVariables: { [name: string]: Field } = {};
                 usedDocuments.forEach(index => capturedVariables[`d${index}`] = docs[index].props.Document);
                 return { capturedVariables };
@@ -60,6 +60,6 @@ export class DocumentIconContainer extends React.Component {
         };
     }
     render() {
-        return DocumentManager.Instance.DocumentViews.map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
+        return Array.from(DocumentManager.Instance.DocumentViews).map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 14ededaf8..6e4d6a52e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -242,7 +242,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
     @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
 
-
     componentWillUnmount() { this.cleanupHandlers(true); }
     componentDidMount() { this.setupHandlers(); }
     //componentDidUpdate() { this.setupHandlers(); }
@@ -1126,9 +1125,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : Doc.DocBrushStatus.unbrushed) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        if (highlightIndex === 6) {
-            console.log("")
-        }
         const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d29160458..b21a28d8e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -231,7 +231,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         const script = ScriptCast(this.rootDoc.script);
-        if (!script) { return null };
+        if (!script) { return null; }
 
         let noviceList: string[] = [];
         let text: string | undefined;
@@ -459,7 +459,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const buttonText = StrCast(this.rootDoc.buttonText);
 
         // TODO:glr Add label of button type
-        let button = this.defaultButton;
+        let button: JSX.Element | null = this.defaultButton;
 
         switch (this.type) {
             case ButtonType.TextButton:
@@ -527,9 +527,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
 
         return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
-            <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
-                {button}
-            </Tooltip>;
+            button !== null ?
+                <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+                    {button}
+                </Tooltip > : null
     }
 }
 
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 6a3f9ed00..bb3791f1e 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -86,7 +86,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
         const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
         const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
-        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" }
+        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" };
     }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
@@ -241,8 +241,8 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         const hideMenu = () => {
             this.fadeOut(true);
             document.removeEventListener("pointerdown", hideMenu);
-        }
-        document.addEventListener("pointerdown", hideMenu)
+        };
+        document.addEventListener("pointerdown", hideMenu);
     }
     render() {
         const buttons = [
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ce82821b6..434fccf8d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1202,7 +1202,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
             } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
-                selectAll(this._editorView.state, this._editorView?.dispatch)
+                selectAll(this._editorView.state, this._editorView?.dispatch);
                 this.startUndoTypingBatch();
             } else if (this._editorView) {
                 this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f4e792acd..0af7715fe 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2435,9 +2435,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             indexNum += (index[i] * (10 ** (-i)));
         }
         this._treeViewMap.set(indexNum, treeViewDoc);
-        console.log(String(index), treeViewDoc)
         this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap)); // this is a flat array of Docs
-        console.log(this.props.Document.presentationLinearizedDocuments)
         return this.childDocs;
     }
 
@@ -2447,21 +2445,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             indexNum += (index[i] * (10 ** (-i)));
         }
         console.log(String(index), treeViewDoc)
-        this._treeViewMap.delete(indexNum)
+        this._treeViewMap.delete(indexNum);
         this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap));
         return this.childDocs;
     }
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<number, Doc>): Doc[] => {
-        // TODO
-        const sortedMap = [...treeViewMap.entries()].sort();
-        var sortedDocs = [];
-        for (var kv of sortedMap) {
-            sortedDocs.push(kv[1]);
-        }
-        return sortedDocs;
-    }
+    sort = (treeViewMap: Map<number, Doc>) => [...treeViewMap.entries()].sort().map(kv => kv[1]);
 
     render() {
         // calling this method for keyEvents
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6db36f36a..d5fd425ad 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -154,7 +154,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         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, NumCast(this.props.Document.scrollHeight));
-            if (scrollTo !== undefined && scrollTo != this.props.layoutDoc._scrollTop) {
+            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
 
                 if (!this._pdfViewer) this._initialScroll = scrollTo;
-- 
cgit v1.2.3-70-g09d2


From b51b78c641c3e64f04cf878f02b5d7b1a620769e Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sun, 5 Jun 2022 11:38:46 -0400
Subject: cleanup

---
 src/Utils.ts                                       |  6 ----
 src/client/util/InteractionUtils.tsx               |  3 +-
 src/client/views/GestureOverlay.tsx                | 10 ++-----
 src/client/views/InkTranscription.tsx              | 22 +++-----------
 src/client/views/InkingStroke.tsx                  |  1 -
 src/client/views/LightboxView.tsx                  |  4 +--
 src/client/views/Touchable.tsx                     |  2 --
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  5 +---
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 src/client/views/nodes/DocumentLinksButton.tsx     |  3 --
 src/client/views/nodes/EquationBox.scss            | 34 ----------------------
 src/client/views/nodes/button/FontIconBox.tsx      |  7 ++---
 12 files changed, 13 insertions(+), 85 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 8c0e8c7c0..523f4eb4f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -5,12 +5,6 @@ import { Socket } from 'socket.io';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
 import Color = require('color');
-import { InkTool } from './fields/InkField';
-import { action } from 'mobx';
-import { CurrentUserUtils } from './client/util/CurrentUserUtils';
-import { CollectionFreeFormView } from './client/views/collections/collectionFreeForm';
-import { WidthSym, HeightSym } from './fields/Doc';
-import { NumCast } from './fields/Types';
 
 export namespace Utils {
     export let DRAG_THRESHOLD = 4;
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index e2c5fab64..289c5bc51 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -286,7 +286,7 @@ export namespace InteractionUtils {
             // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
             case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
             case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
-            case TOUCHTYPE: 
+            case TOUCHTYPE:
                 return e.pointerType === TOUCHTYPE;
             default: return e.pointerType === type;
         }
@@ -357,7 +357,6 @@ export namespace InteractionUtils {
     }
 
     export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: React.Touch[], leniency: number): boolean {
-        console.log("getting here");
         for (const touch of newTouches) {
             if (touch) {
                 const oldTouch = oldTouches.get(touch.identifier);
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 566b7b65a..21b5a8d01 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -18,13 +18,13 @@ import { SelectionManager } from "../util/SelectionManager";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
 import "./GestureOverlay.scss";
-import { ActiveArrowEnd, ActiveArrowStart, ActiveArrowScale, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke";
+import { ActiveArrowEnd, ActiveArrowScale, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke";
+import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
 import { DocumentView } from "./nodes/DocumentView";
 import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -210,7 +210,7 @@ export class GestureOverlay extends Touchable {
         const nts: any = this.getNewTouches(e);
         this._holdTimer && clearTimeout(this._holdTimer);
         this._holdTimer = undefined;
-        
+
         document.dispatchEvent(
             new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
                 {
@@ -296,9 +296,6 @@ export class GestureOverlay extends Touchable {
         else if (thumb.clientX === leftMost) {
             pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v);
         }
-        else {
-            // console.log("not hand");
-        }
         this.pointerIdentifier = pointer?.identifier;
 
         runInAction(() => {
@@ -632,7 +629,6 @@ export class GestureOverlay extends Touchable {
                 if (!actionPerformed) {
                     const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
                     newPoints.pop();
-                    // console.log("getting to bezier math");
                     const controlPoints: { X: number, Y: number }[] = [];
 
                     const bezierCurves = fitCurve(newPoints, 10);
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 5e0667bed..6babb3915 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -4,12 +4,12 @@ import * as React from 'react';
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
 import { InkData, InkField } from "../../fields/InkField";
 import { Cast, DateCast, NumCast } from '../../fields/Types';
-import { DocumentType } from "../documents/DocumentTypes";
-import './InkTranscription.scss';
 import { aggregateBounds } from '../../Utils';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { DocumentType } from "../documents/DocumentTypes";
 import { DocumentManager } from "../util/DocumentManager";
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { InkingStroke } from './InkingStroke';
+import './InkTranscription.scss';
 
 
 export class InkTranscription extends React.Component {
@@ -21,7 +21,6 @@ export class InkTranscription extends React.Component {
     @observable _textRef: any;
     private lastJiix: any;
     private currGroup?: Doc;
-    private containingLayout?: Doc;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -104,29 +103,22 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
+    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
         if (!groupDoc) return;
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
         const strokes: InkData[] = [];
         const times: number[] = [];
-        // console.log(validInks);
         validInks.filter(i => Cast(i.data, InkField)).forEach(i => {
             const d = Cast(i.data, InkField, null);
-            // const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
-            // const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
-            // strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
             const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke;
             strokes.push(d.inkData.map(pd => (inkStroke.ptToScreen({ X: pd.X, Y: pd.Y }))));
             times.push(DateCast(i.creationDate).getDate().getTime());
         });
 
         this.currGroup = groupDoc;
-        this.containingLayout = containingLayout;
 
         const pointerData = { "events": strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) };
-        // console.log(JSON.stringify(pointerData));
-        // console.log(pointerData);
         const processGestures = false;
 
         if (math) {
@@ -225,19 +217,15 @@ export class InkTranscription extends React.Component {
                 newCollection.title = word;
             }
             // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-            // console.log(newCollection);
             newCollection && docView.props.addDocument?.(newCollection);
         });
     }
 
     exportInk = (e: any, ref: any) => {
         const exports = e.detail.exports;
-        // console.log(e);
         if (exports) {
             if (exports['application/x-latex']) {
                 const latex = exports['application/x-latex'];
-                // console.log(latex);
-
                 if (this.currGroup) {
                     this.currGroup.text = latex;
                     this.currGroup.title = latex;
@@ -282,10 +270,8 @@ export class InkTranscription extends React.Component {
                     }
                 }
                 const text = exports['text/plain'];
-                // console.log(text);
 
                 if (this.currGroup) {
-                    // console.log("curr grouping");
                     this.currGroup.transcription = text;
                     this.currGroup.title = text.split("\n")[0];
                 }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 6c95f0f77..40fe6aa68 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -253,7 +253,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
             (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
         const screenHdlPts = screenPts;
-        console.log(screenPts);
 
         const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
         const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index c8f6b5f0b..40c68878d 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -5,7 +5,7 @@ import "normalize.css";
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
@@ -16,8 +16,6 @@ import { TabDocView } from './collections/TabDocView';
 import "./LightboxView.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
-import { CollectionMenu } from './collections/CollectionMenu';
-import { utils } from 'mocha';
 
 interface LightboxViewProps {
     PanelWidth: number;
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index c712e7ed3..789756a78 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -127,13 +127,11 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
-        console.log("getting to handle1PointersMove");
         e.stopPropagation();
         e.preventDefault();
     }
 
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
-        console.log("getting to handle2PointersMove");
         e.stopPropagation();
         e.preventDefault();
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ca8073dac..f292bd96e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -17,7 +17,7 @@ import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { DocServer } from "../../../DocServer";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -50,7 +50,6 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { InkTranscription } from "../../InkTranscription";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -700,7 +699,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        console.log("this is onPointerMove");
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.UserDoc().activeInkTool = InkTool.None;
@@ -823,7 +821,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        console.log("getting here yeet");
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index f762c6619..b60742eba 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -267,7 +267,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             MarqueeOptionsMenu.Instance.createCollection = this.collection;
             MarqueeOptionsMenu.Instance.delete = this.delete;
             MarqueeOptionsMenu.Instance.summarize = this.summary;
-            // MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
             MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
             MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
             MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index f9d2b9917..4a06354b2 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -20,9 +20,6 @@ import { DocumentView } from "./DocumentView";
 import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
 import { TaskCompletionBox } from "./TaskCompletedBox";
 import React = require("react");
-import { Transform } from "../../util/Transform";
-import { InkTool } from "../../../fields/InkField";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 229a1a485..c6a497831 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -2,38 +2,4 @@
 
 .equationBox-cont {
     transform-origin: top left;
-    overflow: visible;
-    width: 100%;
-    height: 100%;
-}
-
-.button {
-    position: absolute;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    top: 0;
-    right: 0;
-    width: 20px;
-    height: 20px;
-    border-radius: 5px;
-    background: $dark-gray;
-    color: white;
-
-    svg {
-        width: 12px;
-        height: 12px;
-    }
-}
-
-.ink-editor {
-    top: 20px;
-    min-width: 500px;
-    min-height: 300px;
-    background: $light-gray;
-    pointer-events: all;
-
-    button {
-        float: right;
-    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d87b23d86..d902a422e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { DataSym, Doc, DocListCast, HeightSym, LayoutSym, StrListCast, WidthSym } from '../../../../fields/Doc';
+import { Doc, HeightSym, StrListCast, WidthSym } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { createSchema } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -14,7 +14,6 @@ import { WebField } from '../../../../fields/URLField';
 import { aggregateBounds, Utils } from '../../../../Utils';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
-import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -262,7 +261,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
             }
         } catch (e) {
-            // console.log(e);
+            console.log(e);
         }
 
         // Get items to place into the list
@@ -767,7 +766,7 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
             // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
             ffView.unprocessedDocs = [];
 
-            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
+            InkTranscription.Instance.transcribeInk(newCollection, selected, false, ffView);
         });
     }
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
-- 
cgit v1.2.3-70-g09d2


From bf8f127f5aa906280fa12dc1ea2cc01bcf9ff40f Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Sun, 5 Jun 2022 19:41:06 -0400
Subject: refactored writing mode code

---
 src/client/views/InkTranscription.tsx         |  49 +---------
 src/client/views/nodes/button/FontIconBox.tsx | 134 +++++++++++++-------------
 2 files changed, 71 insertions(+), 112 deletions(-)

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

diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 54c899144..231ab694d 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -10,6 +10,7 @@ import { aggregateBounds } from '../../Utils';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentManager } from "../util/DocumentManager";
 import { InkingStroke } from './InkingStroke';
+import { groupInkDocs } from './nodes/button/FontIconBox';
 
 
 export class InkTranscription extends React.Component {
@@ -173,53 +174,7 @@ export class InkTranscription extends React.Component {
             if (!docView) return;
             const marqViewRef = docView._marqueeViewRef.current;
             if (!marqViewRef) return;
-            //  loop through selected an get the bound
-            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-            selected.map(action(d => {
-                const x = NumCast(d.x);
-                const y = NumCast(d.y);
-                const width = d[WidthSym]();
-                const height = d[HeightSym]();
-                bounds.push({ x, y, width, height });
-            }))
-
-            const aggregBounds = aggregateBounds(bounds, 0, 0);
-
-            if (marqViewRef) {
-                marqViewRef._downX = aggregBounds.x;
-                marqViewRef._downY = aggregBounds.y;
-                marqViewRef._lastX = aggregBounds.r;
-                marqViewRef._lastY = aggregBounds.b;
-            }
-
-            // set the vals for bounds in marqueeView
-
-            selected.map(action(d => {
-                const dx = NumCast(d.x);
-                const dy = NumCast(d.y);
-                delete d.x;
-                delete d.y;
-                delete d.activeFrame;
-                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                // calculate pos based on bounds
-                if (marqViewRef?.Bounds) {
-                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-                }
-                return d;
-            }));
-
-            docView.props.removeDocument?.(selected);
-            const newCollection = marqViewRef?.getCollection(selected, undefined, true);
-            if (newCollection) {
-                newCollection.height = newCollection[HeightSym]();
-                newCollection.width = newCollection[WidthSym]();
-                newCollection.title = word;
-            }
-            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-            newCollection && docView.props.addDocument?.(newCollection);
+            groupInkDocs(selected, docView, word);
         });
     }
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 3e18e86b3..7a527b767 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -706,76 +706,16 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
     else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
 });
 
-
-
-
-export function checkInksToGroup() {
-    // console.log("getting here to inks group");
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
-        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-            // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
-            const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
-                // console.log(inkDoc.x, inkDoc.y);
-            });
-        });
-    }
-}
-
-export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
+/**
+ * Creates the ink grouping once the user leaves the writing mode
+ */
+export function createInkGroup() {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
-            // loop through selected an get the bound
-            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-            selected.map(action(d => {
-                const x = NumCast(d.x);
-                const y = NumCast(d.y);
-                const width = d[WidthSym]();
-                const height = d[HeightSym]();
-                bounds.push({ x, y, width, height });
-            }))
-
-            const aggregBounds = aggregateBounds(bounds, 0, 0);
-            const marqViewRef = ffView._marqueeViewRef.current;
-
-            // set the vals for bounds in marqueeView
-            if (marqViewRef) {
-                marqViewRef._downX = aggregBounds.x;
-                marqViewRef._downY = aggregBounds.y;
-                marqViewRef._lastX = aggregBounds.r;
-                marqViewRef._lastY = aggregBounds.b;
-            }
-
-            selected.map(action(d => {
-                const dx = NumCast(d.x);
-                const dy = NumCast(d.y);
-                delete d.x;
-                delete d.y;
-                delete d.activeFrame;
-                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                // calculate pos based on bounds
-                if (marqViewRef?.Bounds) {
-                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-                }
-                return d;
-            }));
-            ffView.props.removeDocument?.(selected);
-            // TODO: nda - this is the code to actually get a new grouped collection
-            const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
-            if (newCollection) {
-                newCollection.height = newCollection[HeightSym]();
-                newCollection.width = newCollection[WidthSym]();
-            }
-
-            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-            newCollection && ffView.props.addDocument?.(newCollection);
-            // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
+            const newCollection = groupInkDocs(selected, ffView);
             ffView.unprocessedDocs = [];
 
             InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
@@ -784,6 +724,70 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
 }
 
+/**
+ * Creates the groupings for a given list of ink docs on a specific doc view
+ * @param selected: the list of ink docs to create a grouping of
+ * @param docView: the view in which we want the grouping to be created
+ * @param word: optional param if the group we are creating is a word (subgrouping individual words)
+ * @returns a new collection Doc or undefined if the grouping fails
+ */
+export function groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
+    const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+    // calculate the necessary bounds from the selected ink docs
+    selected.map(action(d => {
+        const x = NumCast(d.x);
+        const y = NumCast(d.y);
+        const width = d[WidthSym]();
+        const height = d[HeightSym]();
+        bounds.push({ x, y, width, height });
+    }))
+
+    // calculate the aggregated bounds
+    const aggregBounds = aggregateBounds(bounds, 0, 0);
+    const marqViewRef = docView._marqueeViewRef.current;
+
+    // set the vals for bounds in marqueeView
+    if (marqViewRef) {
+        marqViewRef._downX = aggregBounds.x;
+        marqViewRef._downY = aggregBounds.y;
+        marqViewRef._lastX = aggregBounds.r;
+        marqViewRef._lastY = aggregBounds.b;
+    }
+
+    // map through all the selected ink strokes and create the groupings
+    selected.map(action(d => {
+        const dx = NumCast(d.x);
+        const dy = NumCast(d.y);
+        delete d.x;
+        delete d.y;
+        delete d.activeFrame;
+        delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        // calculate pos based on bounds
+        if (marqViewRef?.Bounds) {
+            d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+            d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+        }
+        return d;
+    }));
+    docView.props.removeDocument?.(selected);
+    // Gets a collection based on the selected nodes using a marquee view ref
+    const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+    if (newCollection) {
+        newCollection.height = newCollection[HeightSym]();
+        newCollection.width = newCollection[WidthSym]();
+        // if the grouping we are creating is an individual word
+        if (word) {
+            newCollection.title = word;
+        }
+    }
+
+    // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+    newCollection && docView.props.addDocument?.(newCollection);
+    return newCollection;
+}
+
 
 /** INK
  *      setActiveInkTool
-- 
cgit v1.2.3-70-g09d2


From 2e42d66be1439de347305cfd43c6f7e7042127c6 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Sun, 5 Jun 2022 20:12:28 -0400
Subject: refactored writing mode code into InKTranscription

---
 src/client/views/GestureOverlay.tsx           |  8 +--
 src/client/views/InkTranscription.tsx         | 88 ++++++++++++++++++++++++++-
 src/client/views/nodes/button/FontIconBox.tsx | 86 +-------------------------
 3 files changed, 89 insertions(+), 93 deletions(-)

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

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 226983138..1a7bb0808 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -24,7 +24,7 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
+import { InkTranscription } from "./InkTranscription";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -127,7 +127,7 @@ export class GestureOverlay extends Touchable {
             // pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
-                createInkGroup();
+                InkTranscription.Instance.createInkGroup();
                 Doc.UserDoc().activeInkTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
@@ -499,7 +499,7 @@ export class GestureOverlay extends Touchable {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
-                    createInkGroup();
+                    InkTranscription.Instance.createInkGroup();
                     CurrentUserUtils.SelectedTool = InkTool.None;
                     return;
                 }
@@ -649,8 +649,6 @@ export class GestureOverlay extends Touchable {
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
                     this._points = controlPoints;
                     this.dispatchGesture(GestureUtils.Gestures.Stroke);
-                    // TODO: nda - check inks to group here
-                    checkInksToGroup();
                 }
                 this._points = [];
             }
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 231ab694d..e1a80ae50 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -2,7 +2,7 @@ import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
-import { InkData, InkField } from "../../fields/InkField";
+import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { Cast, DateCast, NumCast } from '../../fields/Types';
 import { DocumentType } from "../documents/DocumentTypes";
 import './InkTranscription.scss';
@@ -10,7 +10,7 @@ import { aggregateBounds } from '../../Utils';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentManager } from "../util/DocumentManager";
 import { InkingStroke } from './InkingStroke';
-import { groupInkDocs } from './nodes/button/FontIconBox';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
 
 
 export class InkTranscription extends React.Component {
@@ -174,7 +174,7 @@ export class InkTranscription extends React.Component {
             if (!docView) return;
             const marqViewRef = docView._marqueeViewRef.current;
             if (!marqViewRef) return;
-            groupInkDocs(selected, docView, word);
+            this.groupInkDocs(selected, docView, word);
         });
     }
 
@@ -238,6 +238,88 @@ export class InkTranscription extends React.Component {
             }
         }
     }
+    
+    /**
+ * Creates the ink grouping once the user leaves the writing mode
+ */
+createInkGroup() {
+    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
+    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+            const selected = ffView.unprocessedDocs;
+            const newCollection = this.groupInkDocs(selected, ffView);
+            ffView.unprocessedDocs = [];
+
+            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
+        });
+    }
+    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+}
+
+/**
+ * Creates the groupings for a given list of ink docs on a specific doc view
+ * @param selected: the list of ink docs to create a grouping of
+ * @param docView: the view in which we want the grouping to be created
+ * @param word: optional param if the group we are creating is a word (subgrouping individual words)
+ * @returns a new collection Doc or undefined if the grouping fails
+ */
+  groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
+    const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+    // calculate the necessary bounds from the selected ink docs
+    selected.map(action(d => {
+        const x = NumCast(d.x);
+        const y = NumCast(d.y);
+        const width = d[WidthSym]();
+        const height = d[HeightSym]();
+        bounds.push({ x, y, width, height });
+    }))
+
+    // calculate the aggregated bounds
+    const aggregBounds = aggregateBounds(bounds, 0, 0);
+    const marqViewRef = docView._marqueeViewRef.current;
+
+    // set the vals for bounds in marqueeView
+    if (marqViewRef) {
+        marqViewRef._downX = aggregBounds.x;
+        marqViewRef._downY = aggregBounds.y;
+        marqViewRef._lastX = aggregBounds.r;
+        marqViewRef._lastY = aggregBounds.b;
+    }
+
+    // map through all the selected ink strokes and create the groupings
+    selected.map(action(d => {
+        const dx = NumCast(d.x);
+        const dy = NumCast(d.y);
+        delete d.x;
+        delete d.y;
+        delete d.activeFrame;
+        delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        // calculate pos based on bounds
+        if (marqViewRef?.Bounds) {
+            d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+            d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+        }
+        return d;
+    }));
+    docView.props.removeDocument?.(selected);
+    // Gets a collection based on the selected nodes using a marquee view ref
+    const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+    if (newCollection) {
+        newCollection.height = newCollection[HeightSym]();
+        newCollection.width = newCollection[WidthSym]();
+        // if the grouping we are creating is an individual word
+        if (word) {
+            newCollection.title = word;
+        }
+    }
+
+    // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+    newCollection && docView.props.addDocument?.(newCollection);
+    return newCollection;
+}
 
     render() {
         return (
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 7a527b767..e16c055e4 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -706,89 +706,6 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
     else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
 });
 
-/**
- * Creates the ink grouping once the user leaves the writing mode
- */
-export function createInkGroup() {
-    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
-        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-            const selected = ffView.unprocessedDocs;
-            const newCollection = groupInkDocs(selected, ffView);
-            ffView.unprocessedDocs = [];
-
-            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
-        });
-    }
-    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
-}
-
-/**
- * Creates the groupings for a given list of ink docs on a specific doc view
- * @param selected: the list of ink docs to create a grouping of
- * @param docView: the view in which we want the grouping to be created
- * @param word: optional param if the group we are creating is a word (subgrouping individual words)
- * @returns a new collection Doc or undefined if the grouping fails
- */
-export function groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
-    const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-    // calculate the necessary bounds from the selected ink docs
-    selected.map(action(d => {
-        const x = NumCast(d.x);
-        const y = NumCast(d.y);
-        const width = d[WidthSym]();
-        const height = d[HeightSym]();
-        bounds.push({ x, y, width, height });
-    }))
-
-    // calculate the aggregated bounds
-    const aggregBounds = aggregateBounds(bounds, 0, 0);
-    const marqViewRef = docView._marqueeViewRef.current;
-
-    // set the vals for bounds in marqueeView
-    if (marqViewRef) {
-        marqViewRef._downX = aggregBounds.x;
-        marqViewRef._downY = aggregBounds.y;
-        marqViewRef._lastX = aggregBounds.r;
-        marqViewRef._lastY = aggregBounds.b;
-    }
-
-    // map through all the selected ink strokes and create the groupings
-    selected.map(action(d => {
-        const dx = NumCast(d.x);
-        const dy = NumCast(d.y);
-        delete d.x;
-        delete d.y;
-        delete d.activeFrame;
-        delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-        delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-        // calculate pos based on bounds
-        if (marqViewRef?.Bounds) {
-            d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-            d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-        }
-        return d;
-    }));
-    docView.props.removeDocument?.(selected);
-    // Gets a collection based on the selected nodes using a marquee view ref
-    const newCollection = marqViewRef?.getCollection(selected, undefined, true);
-    if (newCollection) {
-        newCollection.height = newCollection[HeightSym]();
-        newCollection.width = newCollection[WidthSym]();
-        // if the grouping we are creating is an individual word
-        if (word) {
-            newCollection.title = word;
-        }
-    }
-
-    // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-    newCollection && docView.props.addDocument?.(newCollection);
-    return newCollection;
-}
-
-
 /** INK
  *      setActiveInkTool
  *      setStrokeWidth
@@ -796,8 +713,7 @@ export function groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, w
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
-    createInkGroup();
-
+    InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
-- 
cgit v1.2.3-70-g09d2

