From f1f341b53a1a49427cf7ef40afdcd95a9b0e4e9d Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 17 Mar 2022 16:35:30 -0400
Subject: feat: working on implementing writing mode

---
 src/client/views/GestureOverlay.tsx | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 04abdbf37..130e71924 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -617,20 +617,20 @@ export class GestureOverlay extends Touchable {
                     newPoints.pop();
                     const controlPoints: { X: number, Y: number }[] = [];
 
-                    const bezierCurves = fitCurve(newPoints, 10);
-                    for (const curve of bezierCurves) {
+                    // const bezierCurves = fitCurve(newPoints, 10);
+                    // for (const curve of bezierCurves) {
 
-                        controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
-                        controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
-                        controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
-                        controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
+                    //     controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
+                    //     controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
+                    //     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;
+                    // }
+                    // 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.dispatchGesture(GestureUtils.Gestures.Stroke);
                 }
-- 
cgit v1.2.3-70-g09d2


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/Utils.ts                                       |  1 +
 src/client/util/CurrentUserUtils.ts                |  1 +
 src/client/views/GestureOverlay.tsx                | 23 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 67 +++++++++++++++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  3 +
 src/client/views/nodes/button/FontIconBox.tsx      | 83 +++++++++++++++++++++-
 6 files changed, 161 insertions(+), 17 deletions(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index d0d891f77..6519b5d13 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -407,6 +407,7 @@ export function formatTime(time: number) {
     return (hours ? hours.toString() + ":" : "") + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
 }
 
+// x is furthest left, y is furthest top, r is furthest right, b is furthest bottom
 export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) {
     const bounds = boundsList.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) })).reduce((bounds, b) => ({
         x: Math.min(b.x, bounds.x), y: Math.min(b.y, bounds.y),
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c7f293f2c..6cab8c100 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1011,6 +1011,7 @@ 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: "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 05e5b7d5f..87b006a93 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -617,22 +617,23 @@ export class GestureOverlay extends Touchable {
                     newPoints.pop();
                     const controlPoints: { X: number, Y: number }[] = [];
 
-                    // const bezierCurves = fitCurve(newPoints, 10);
-                    // for (const curve of bezierCurves) {
+                    const bezierCurves = fitCurve(newPoints, 10);
+                    for (const curve of bezierCurves) {
 
-                    //     controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
-                    //     controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
-                    //     controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
-                    //     controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
+                        controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
+                        controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
+                        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;
+                    }
+                    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.dispatchGesture(GestureUtils.Gestures.Stroke);
+                    
                 }
                 this._points = [];
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e2ea81392..a694ca2b3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -3,7 +3,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { DateField } from "../../../../fields/DateField";
-import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
@@ -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, DocUtils } from "../../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -97,6 +97,9 @@ 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[] = []; 
 
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
@@ -114,6 +117,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _deleteList: DocumentView[] = [];
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
+    @observable _marqueeViewRef = React.createRef<MarqueeView>();
     @observable _keyframeEditing = false;
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
@@ -431,6 +435,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return false;
     }
 
+    @action
+    onPenUp = (e: PointerEvent): void => {
+        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+            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);
+
+            this._batch?.end();
+        }
+    }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = this._lastX = e.pageX;
@@ -442,7 +466,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 switch (CurrentUserUtils.SelectedTool) {
                     case InkTool.Highlighter:
-                    case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+                        break;
+                        // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
+                    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);
                         document.addEventListener("pointerup", this.onEraserUp);
@@ -486,6 +535,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
+    public unprocessedDocs: Doc[] =[];
+    public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
     @undoBatch
     onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
         switch (ge.gesture) {
@@ -494,7 +545,14 @@ 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() });
-                this.addDocument(inkDoc);
+                // 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);
+                    this.addDocument(inkDoc);
+                // } else this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
             case GestureUtils.Gestures.Box:
@@ -1598,6 +1656,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         TraceMobx();
         return <MarqueeView
             {...this.props}
+            ref={this._marqueeViewRef}
             ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
             nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
             addDocTab={this.addDocTab}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b10b0912f..eeb2b653b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -60,7 +60,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @computed get Transform() { return this.props.getTransform(); }
     @computed get Bounds() {
+        // nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+        // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
         const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
         return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
     }
@@ -437,6 +439,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
+        // TODO: nda - this is the code to actually get a new grouped collection
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
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/GestureOverlay.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 af94a0a883694e7147f227911264f59ec322a23b Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 7 Apr 2022 17:41:05 -0400
Subject: added todo comment

---
 src/client/views/GestureOverlay.tsx                             | 2 ++
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 53b360e60..54fb50db9 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -73,6 +73,8 @@ export class GestureOverlay extends Touchable {
         this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
     }
 
+    // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
+
     /**
      * Ignores all touch events that belong to a hand being held down.
      */
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 94f2d17de..984e22d95 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 || InkTool.Write ? "crosshair" : "pointer"
+                cursor: CurrentUserUtils.SelectedTool === InkTool.Pen || CurrentUserUtils.SelectedTool === InkTool.Write ? "crosshair" : "pointer"
             }}
 
             onDragOver={e => e.preventDefault()}
-- 
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/GestureOverlay.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 ab0e1ac6f5b21f2e10bdce428c882482a0f159b4 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 12 Apr 2022 18:09:03 -0400
Subject: ink to text for groupings

---
 package-lock.json                                  |  79 ++++++++-
 package.json                                       |   1 +
 src/client/documents/Documents.ts                  |   5 +-
 src/client/views/GestureOverlay.tsx                |  16 +-
 src/client/views/InkTranscription.scss             |   5 +
 src/client/views/InkTranscription.tsx              | 188 +++++++++++++++++++++
 src/client/views/MainView.tsx                      |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  19 ++-
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |  11 --
 .../collections/collectionFreeForm/MarqueeView.tsx |  16 +-
 src/client/views/nodes/EquationBox.scss            |  36 ++++
 src/client/views/nodes/EquationBox.tsx             |  91 +++++++++-
 src/fields/InkField.ts                             |   3 +-
 src/pen-gestures/GestureUtils.ts                   |   3 +-
 src/typings/index.d.ts                             |   1 +
 15 files changed, 444 insertions(+), 34 deletions(-)
 create mode 100644 src/client/views/InkTranscription.scss
 create mode 100644 src/client/views/InkTranscription.tsx

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/package-lock.json b/package-lock.json
index b1431e55b..1f8b1b8f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4579,6 +4579,16 @@
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
             "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
         },
+        "clipboard": {
+            "version": "1.7.1",
+            "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
+            "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
+            "requires": {
+                "good-listener": "^1.2.2",
+                "select": "^1.1.2",
+                "tiny-emitter": "^2.0.0"
+            }
+        },
         "cliss": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz",
@@ -5307,6 +5317,11 @@
                 }
             }
         },
+        "crypto-js": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz",
+            "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q=="
+        },
         "crypto-random-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@@ -5885,6 +5900,11 @@
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
             "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
         },
+        "delegate": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+            "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+        },
         "delegates": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -7846,6 +7866,14 @@
                 "jquery": "*"
             }
         },
+        "good-listener": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+            "requires": {
+                "delegate": "^3.1.2"
+            }
+        },
         "google-auth-library": {
             "version": "4.2.6",
             "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-4.2.6.tgz",
@@ -8462,6 +8490,29 @@
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
             "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
         },
+        "iink-js": {
+            "version": "1.5.4",
+            "resolved": "https://registry.npmjs.org/iink-js/-/iink-js-1.5.4.tgz",
+            "integrity": "sha512-WcjMmyXbSo4GY56AVVTKRyxlguh2KAGFyoH24+Zmm87ZWII12or2uLiovAyOK4IE+VVz7m0U5RMuv8i/RV/3Nw==",
+            "requires": {
+                "@babel/runtime": "^7.9.2",
+                "clipboard": "^1.7.1",
+                "crypto-js": "^3.3.0",
+                "d3-selection": "^1.4.1",
+                "json-css": "^1.5.6",
+                "lodash.merge": "^4.6.2",
+                "loglevel": "^1.6.8",
+                "perfect-scrollbar": "^1.5.0",
+                "uuid-js": "^0.7.5"
+            },
+            "dependencies": {
+                "d3-selection": {
+                    "version": "1.4.2",
+                    "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz",
+                    "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg=="
+                }
+            }
+        },
         "image-data-uri": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.1.tgz",
@@ -9524,6 +9575,11 @@
             "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
             "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
         },
+        "json-css": {
+            "version": "1.5.6",
+            "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz",
+            "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA="
+        },
         "json-parse-better-errors": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -9996,8 +10052,7 @@
         "loglevel": {
             "version": "1.6.8",
             "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
-            "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
-            "dev": true
+            "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA=="
         },
         "loglevelnext": {
             "version": "1.0.5",
@@ -14793,6 +14848,11 @@
             "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
             "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
         },
+        "perfect-scrollbar": {
+            "version": "1.5.5",
+            "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz",
+            "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g=="
+        },
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -16791,6 +16851,11 @@
             "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
             "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
         },
+        "select": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+        },
         "select-hose": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -18208,6 +18273,11 @@
             "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
             "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
         },
+        "tiny-emitter": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+            "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+        },
         "tiny-inflate": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
@@ -19185,6 +19255,11 @@
             "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
             "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
         },
+        "uuid-js": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
+            "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
+        },
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
diff --git a/package.json b/package.json
index 2c3e641dd..c8455c8a3 100644
--- a/package.json
+++ b/package.json
@@ -196,6 +196,7 @@
         "https": "^1.0.0",
         "https-browserify": "^1.0.0",
         "i": "^0.3.7",
+        "iink-js": "^1.5.4",
         "image-data-uri": "^2.0.1",
         "image-size": "^0.7.5",
         "image-size-stream": "^1.1.0",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9bcd23aa0..884cc781d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -4,7 +4,7 @@ import { DateField } from "../../fields/DateField";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Initializing, Opt, updateCachedAcls, WidthSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { HtmlField } from "../../fields/HtmlField";
-import { InkField } from "../../fields/InkField";
+import { InkField, PointData } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { ProxyField } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
@@ -738,7 +738,7 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
@@ -1161,6 +1161,7 @@ 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 50dca0a99..16a9cfaeb 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -41,6 +41,7 @@ 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;
@@ -59,6 +60,7 @@ 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;
 
@@ -418,6 +420,7 @@ export class GestureOverlay extends Touchable {
 
             this._strokes = [];
             this._points = [];
+            this._timeStamps = [];
             this._possibilities = [];
             document.removeEventListener("touchend", this.handleHandUp);
         }
@@ -493,6 +496,8 @@ export class GestureOverlay extends Touchable {
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             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)");
         }
@@ -501,6 +506,7 @@ 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;
@@ -547,6 +553,7 @@ 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);
@@ -558,6 +565,7 @@ 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[] = [];
@@ -581,6 +589,7 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.IgnoreGesture:
                         this.dispatchGesture(GestureUtils.Gestures.Stroke);
                         this._points = [];
+                        this._timeStamps = [];
                         break;
                 }
             }
@@ -589,6 +598,7 @@ 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;
@@ -635,9 +645,11 @@ export class GestureOverlay extends Touchable {
                     this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 }
                 this._points = [];
+                this._timeStamps = [];
             }
         } else {
             this._points = [];
+            this._timeStamps = [];
         }
         CollectionFreeFormViewChrome.Instance?.primCreated();
     }
@@ -686,6 +698,7 @@ 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)
@@ -808,7 +821,8 @@ export class GestureOverlay extends Touchable {
                         points: stroke ?? this._points,
                         gesture: gesture as any,
                         bounds: this.getBounds(stroke ?? this._points),
-                        text: data
+                        text: data,
+                        times: this._timeStamps
                     }
                 }
             )
diff --git a/src/client/views/InkTranscription.scss b/src/client/views/InkTranscription.scss
new file mode 100644
index 000000000..bbb0a1afa
--- /dev/null
+++ b/src/client/views/InkTranscription.scss
@@ -0,0 +1,5 @@
+.ink-transcription {
+    .error-msg {
+        display: none !important;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
new file mode 100644
index 000000000..950f622dd
--- /dev/null
+++ b/src/client/views/InkTranscription.tsx
@@ -0,0 +1,188 @@
+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 { InkData, InkField } from "../../fields/InkField";
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Docs } from "../documents/Documents";
+import './InkTranscription.scss';
+
+export class InkTranscription extends React.Component {
+    static Instance: InkTranscription;
+
+    @observable _mathRegister: any;
+    @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 };
+
+    constructor(props: Readonly<{}>) {
+        super(props);
+
+        InkTranscription.Instance = this;
+    }
+
+    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
+    setMathRef = (r: any) => {
+        if (!this._mathRegister) {
+            this._mathRegister = r ? iink.register(r, {
+                recognitionParams: {
+                    type: 'MATH',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                        websocket: {
+                            pingEnabled: false,
+                            autoReconnect: true
+                        }
+                    },
+                    iink: {
+                        math: {
+                            mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                },
+                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;
+    }
+
+    @action
+    setTextRef = (r: any) => {
+        if (!this._textRegister) {
+            this._textRegister = r ? iink.register(r, {
+                recognitionParams: {
+                    type: 'TEXT',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                        websocket: {
+                            pingEnabled: false,
+                            autoReconnect: true
+                        }
+                    },
+                    iink: {
+                        text: {
+                            mimeTypes: ['text/plain']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                },
+                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) => {
+        const strokes: InkData[] = [];
+        inkdocs.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 })));
+        });
+
+        this.addDocument = addDocument;
+        this.bounds = bounds;
+
+        const pointerData = { "events": strokes.map(stroke => this.inkJSON(stroke)) };
+        // console.log(JSON.stringify(pointerData));
+        const processGestures = false;
+
+        if (math) {
+            this._mathRef.editor.pointerEvents(pointerData, processGestures);
+        }
+        else {
+            this._textRef.editor.pointerEvents(pointerData, processGestures);
+        }
+    }
+
+    callExport(ref: any) {
+        if (ref.editor.canExport) {
+            ref.editor.export_();
+        }
+    }
+
+    inkJSON = (stroke: InkData) => {
+        return {
+            "pointerType": "PEN",
+            "pointerId": 1,
+            "x": stroke.map(point => point.X),
+            "y": stroke.map(point => point.Y),
+            "t": stroke.map(point => point.time),
+            "p": new Array(stroke.length).fill(1.0)
+        };
+    }
+
+    exportInk = (e: any, ref: any) => {
+        const exports = e.detail.exports;
+        if (exports) {
+            if (exports['application/x-latex']) {
+                const latex = exports['application/x-latex'];
+                console.log(latex);
+
+                this.addDocument?.(Docs.Create.EquationDocument({ title: latex, x: this.bounds.width, y: 0, _width: this.bounds.width, _height: this.bounds.height }));
+            }
+            else if (exports['text/plain']) {
+                const text = exports['text/plain'];
+                console.log(text);
+                this.addDocument?.(Docs.Create.TextDocument(text, { title: text, x: this.bounds.width, y: 0, _width: this.bounds.width, _height: this.bounds.height }));
+            }
+
+            ref.editor.clear();
+        }
+    }
+
+    render() {
+        return (
+            <div className="ink-transcription">
+                <div className='math-editor'
+                    ref={this.setMathRef}
+                    touch-action="none">
+                </div>
+                <div className='text-editor'
+                    ref={this.setTextRef}
+                    touch-action="none">
+                </div>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..84c1037dd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -44,6 +44,7 @@ import { GestureOverlay } from './GestureOverlay';
 import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
+import { InkTranscription } from './InkTranscription';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
@@ -184,7 +185,7 @@ export class MainView extends React.Component {
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
         fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
+        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSquareRootAlt]);
         this.initAuthenticationRouters();
     }
 
@@ -636,6 +637,7 @@ export class MainView extends React.Component {
             <OverlayView />
             <TimelineMenu />
             <RichTextMenu />
+            <InkTranscription />
             {this.snapLines}
             <div className="mainView-webRef" ref={this.makeWebRef} />
             <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e2ea81392..7d60387de 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -50,6 +50,7 @@ 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",
@@ -492,7 +493,9 @@ 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 inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
+                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,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
                 this.addDocument(inkDoc);
                 e.stopPropagation();
@@ -1477,6 +1480,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
+
+        this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to text", event: () => this.transcribeStrokes(false), icon: "font" });
+
+        this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
+
         !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
@@ -1536,6 +1544,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         input.click();
     }
 
+    @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.addDocument);
+        }
+    }
+
     @action
     setupDragLines = (snapToDraggedDoc: boolean = false) => {
         const activeDocs = this.getActiveDocuments();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 1f59f9732..8a8b528f6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -14,7 +14,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
     public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction;
     public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
     public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
-    public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
     public showMarquee: () => void = unimplementedFunction;
     public hideMarquee: () => void = unimplementedFunction;
     public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
@@ -64,16 +63,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
                 </button>
             </Tooltip>,
         ];
-        if (false && !SelectionManager.Views().some(v => v.props.Document.type !== DocumentType.INK)) {
-            buttons.push(
-                <Tooltip key="inkToText" title={<div className="dash-tooltip">Change to Text</div>} placement="bottom">
-                    <button
-                        className="antimodeMenu-button"
-                        onPointerDown={this.inkToText}>
-                        <FontAwesomeIcon icon="font" size="lg" />
-                    </button>
-                </Tooltip>);
-        }
         return this.getElement(buttons);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b10b0912f..a828cd981 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,14 +1,15 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
-import { Utils, intersectRect, returnFalse } from "../../../../Utils";
+import { intersectRect, returnFalse, Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -21,18 +22,17 @@ import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
+import { VideoBox } from "../../nodes/VideoBox";
+import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 import { PreviewCursor } from "../../PreviewCursor";
+import { StyleLayers } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { SubCollectionViewProps } from "../CollectionSubView";
 import { CollectionView } from "../CollectionView";
+import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { StyleLayers } from "../../StyleProvider";
-import { TreeView } from "../TreeView";
-import { VideoBox } from "../../nodes/VideoBox";
-import { ImageField, WebField } from "../../../../fields/URLField";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -265,7 +265,7 @@ 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.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/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 6c9d53d10..229a1a485 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,3 +1,39 @@
+@import "../global/globalCssVariables.scss";
+
 .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/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index c170f9867..c0c4fab09 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,5 +1,7 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import EquationEditor from 'equation-editor-react';
-import { action, reaction } from 'mobx';
+import * as iink from 'iink-js';
+import { action, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { WidthSym } from '../../../fields/Doc';
@@ -12,12 +14,16 @@ import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 
-
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
     public static SelectOnLoad: string = "";
+
     _ref: React.RefObject<EquationEditor> = React.createRef();
+    @observable _inkOpen = false;
+    @observable _inkEditor: any;
+    @observable _inkRef: any;
+
     componentDidMount() {
         if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
             this.props.select(false);
@@ -39,7 +45,11 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }, { fireImmediately: true });
     }
-    plot: any;
+
+    componentWillUnmount() {
+        this._inkRef.removeEventListener('exported', this.exportInk);
+    }
+
     @action
     keyPressed = (e: KeyboardEvent) => {
         const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", ""));
@@ -65,6 +75,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
     }
+
     onChange = (str: string) => {
         this.dataDoc.text = str;
         const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
@@ -75,6 +86,58 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc._height = Math.max(25, _height);
         }
     }
+
+    @action
+    toggleInk = (e: React.PointerEvent) => {
+        e.stopPropagation();
+
+        this._inkOpen = !this._inkOpen;
+
+        if (!this._inkEditor) {
+            this._inkEditor = this._inkRef ? iink.register(this._inkRef, {
+                recognitionParams: {
+                    type: 'MATH',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                    },
+                    iink: {
+                        math: {
+                            mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                }
+            }) : null;
+
+            this._inkRef.addEventListener('exported', this.exportInk)
+        }
+    }
+
+    convertInk = (e: React.MouseEvent) => {
+        this._inkRef.editor.convert();
+    }
+
+    clearInk = (e: React.MouseEvent) => {
+        this._inkRef.editor.clear();
+        this.onChange("");
+    }
+
+    exportInk = (e: any) => {
+        const exports = e.detail.exports;
+        if (exports && exports['application/x-latex']) {
+            this.onChange(exports['application/x-latex']);
+            console.log(JSON.parse(exports['application/vnd.myscript.jiix']).expressions[0].items[0]);
+        }
+    }
+
+
     render() {
         TraceMobx();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
@@ -82,18 +145,34 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
             style={{
                 transform: `scale(${scale})`,
-                width: `${100 / scale}%`,
+                width: `calc(${100 / scale}% + 25px)`,
                 height: `${100 / scale}%`,
                 pointerEvents: !this.props.isSelected() ? "none" : undefined,
             }}
-            onKeyDown={e => e.stopPropagation()}
-        >
+            onKeyDown={e => e.stopPropagation()}>
+
             <EquationEditor ref={this._ref}
                 value={this.dataDoc.text || "x"}
                 spaceBehavesLikeTab={true}
                 onChange={this.onChange}
                 autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
                 autoOperatorNames="sin cos tan" />
+
+            <div className="button"
+                style={{ display: this.props.isContentActive() ? "flex" : "none" }}
+                onPointerDown={this.toggleInk}>
+                <FontAwesomeIcon icon="pencil-alt" />
+            </div>
+
+            <div className='ink-editor'
+                ref={action((r: any) => this._inkRef = r)}
+                id="editor" onPointerDown={(e) => e.stopPropagation()}
+                touch-action="none"
+                style={{ display: this._inkOpen && this.props.isContentActive() ? "block" : "none" }}>
+                <button onClick={this.convertInk}>convert</button>
+                <button onClick={this.clearInk}>clear</button>
+            </div>
+
         </div>);
     }
 }
\ No newline at end of file
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 31024e805..48271d3d3 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -19,6 +19,7 @@ export enum InkTool {
 export interface PointData {
     X: number;
     Y: number;
+    time?: number;
 }
 
 export type Segment = Array<Bezier>;
@@ -83,7 +84,7 @@ export class InkField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}} `) + "])";
+        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}, time: ${i.time || 0}} `) + "])";
     }
     [ToString]() {
         return "InkField";
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index 65f2bf80c..cf59cb3c6 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -8,7 +8,8 @@ export namespace GestureUtils {
             readonly gesture: Gestures,
             readonly points: PointData[],
             readonly bounds: Rect,
-            readonly text?: any
+            readonly text?: any,
+            readonly times?: number[]
         ) { }
     }
 
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 068ac2159..bc7727426 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -8,6 +8,7 @@ declare module 'webrtc-adapter';
 declare module 'bezier-curve';
 declare module 'fit-curve';
 declare module 'react-audio-waveform';
+declare module 'iink-js';
 
 declare module 'reveal';
 declare module 'react-reveal';
-- 
cgit v1.2.3-70-g09d2


From 605ab32150c5b2b9d84eb817d3086796b72964df Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 14 Apr 2022 17:46:15 -0400
Subject: fix: fixed panning with finger interaction

---
 src/client/util/InteractionUtils.tsx                        |  2 ++
 src/client/views/GestureOverlay.tsx                         | 13 +++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx           |  2 ++
 3 files changed, 11 insertions(+), 6 deletions(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3e051dec8..675dff00b 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -286,6 +286,8 @@ 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: 
+                return e.pointerType === TOUCHTYPE;
             default: return e.pointerType === type;
         }
     }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index f034e06ad..75a757280 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -85,7 +85,6 @@ 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);
@@ -112,7 +111,6 @@ 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) {
@@ -131,6 +129,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) {
+                Doc.UserDoc().activeInkTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -209,7 +208,6 @@ 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;
@@ -500,6 +498,10 @@ export class GestureOverlay extends Touchable {
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
+        if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+            CurrentUserUtils.SelectedTool = InkTool.None;
+            return;
+        }
         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;
@@ -631,7 +633,8 @@ export class GestureOverlay extends Touchable {
                 }
 
                 // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document
-                if (!actionPerformed) {
+                console.log("dispatch gesture", CurrentUserUtils.SelectedTool);
+                if (!actionPerformed && CurrentUserUtils.SelectedTool !== InkTool.None) {
                     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");
@@ -650,11 +653,9 @@ export class GestureOverlay extends Touchable {
                         (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.dispatchGesture(GestureUtils.Gestures.Stroke);
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
-                    
                 }
                 this._points = [];
                 this._timeStamps = [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 908f08f88..c9465e7a4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -704,6 +704,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerMove = (e: PointerEvent): void => {
         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) {
             if (this.tryDragCluster(e, this._hitCluster)) {
-- 
cgit v1.2.3-70-g09d2


From dd3015568c95fbafdfeace835f82f5032bd61aef Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 14 Apr 2022 18:03:05 -0400
Subject: fix: fixed panning with finger interaction

---
 src/client/views/GestureOverlay.tsx | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 75a757280..5f0311d38 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 } from "./nodes/button/FontIconBox";
+import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -129,6 +129,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();
                 Doc.UserDoc().activeInkTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
@@ -499,6 +500,7 @@ export class GestureOverlay extends Touchable {
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+            createInkGroup();
             CurrentUserUtils.SelectedTool = InkTool.None;
             return;
         }
@@ -633,8 +635,7 @@ export class GestureOverlay extends Touchable {
                 }
 
                 // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document
-                console.log("dispatch gesture", CurrentUserUtils.SelectedTool);
-                if (!actionPerformed && CurrentUserUtils.SelectedTool !== InkTool.None) {
+                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");
@@ -648,7 +649,7 @@ 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];
-- 
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/GestureOverlay.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/GestureOverlay.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 4ac7c6fa8a4adf5a97350d4f0e59a853753e7f75 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 5 May 2022 15:31:35 -0400
Subject: doubletap

---
 src/client/views/GestureOverlay.tsx | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'src/client/views/GestureOverlay.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 13fa62048..566b7b65a 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -497,9 +497,13 @@ export class GestureOverlay extends Touchable {
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            createInkGroup();
-            CurrentUserUtils.SelectedTool = InkTool.None;
-            return;
+            setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
+                if (doubleTap) {
+                    createInkGroup();
+                    CurrentUserUtils.SelectedTool = InkTool.None;
+                    return;
+                }
+            }));
         }
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-- 
cgit v1.2.3-70-g09d2

