From 1638527259a072dfc2ab286bd27bbb1751e8434e Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Fri, 5 Aug 2022 08:30:35 -0500
Subject: stashing to look at master

---
 src/client/views/collections/CollectionNoteTakingViewColumn.tsx | 4 ++++
 1 file changed, 4 insertions(+)

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

diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index bdcb9c399..0ee5985bb 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -144,6 +144,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     }
 
     @action
+    @undoBatch
     deleteColumn = () => {
         if (!this.props.columnHeaders) {
           return
@@ -154,6 +155,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
           const newHeader = this.props.columnHeaders[newIndex];
           this.props.docList.forEach(d => d[this.props.pivotField] = newHeader.heading.toString())
           this.props.columnHeaders.splice(index, 1);
+          // const newHeaders = this.props.columnHeaders;
+          // newHeaders.splice(index, 1);
+          // this.props.columnHeaders = newHeaders;
           this.props.resizeColumns(this.props.columnHeaders.length)
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 92ccfc514a15b03e5a1c0cb7ccbe9c1fe5ce82ad Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Thu, 11 Aug 2022 07:37:03 -0500
Subject: cc

---
 package-lock.json                                  | 39 ++++++++++++++++++++++
 package.json                                       |  7 ++--
 .../collections/CollectionNoteTakingViewColumn.tsx | 19 +++++++----
 3 files changed, 55 insertions(+), 10 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 3b9cda8bb..6149d9314 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5059,6 +5059,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6208,6 +6218,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6219,6 +6251,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -20715,6 +20748,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/package.json b/package.json
index 2e3ef0a07..177d3a5e6 100644
--- a/package.json
+++ b/package.json
@@ -66,17 +66,17 @@
         "@types/prosemirror-view": "^1.23.1",
         "@types/rc-switch": "^1.9.2",
         "@types/react": "^18.0.15",
-        "@types/react-icons": "^3.0.0",
-        "@types/react-reconciler": "^0.26.4",
-        "@types/react-transition-group": "^4.4.5",
         "@types/react-autosuggest": "^9.3.14",
         "@types/react-color": "^2.17.6",
         "@types/react-datepicker": "^3.1.8",
         "@types/react-dom": "^18.0.6",
         "@types/react-grid-layout": "^1.3.2",
+        "@types/react-icons": "^3.0.0",
         "@types/react-measure": "^2.0.8",
+        "@types/react-reconciler": "^0.26.4",
         "@types/react-select": "^3.1.2",
         "@types/react-table": "^6.8.9",
+        "@types/react-transition-group": "^4.4.5",
         "@types/request": "^2.48.8",
         "@types/request-promise": "^4.1.48",
         "@types/rimraf": "^2.0.5",
@@ -84,6 +84,7 @@
         "@types/shelljs": "^0.8.11",
         "@types/socket.io": "^2.1.13",
         "@types/socket.io-client": "^1.4.36",
+        "@types/socket.io-parser": "^3.0.0",
         "@types/typescript": "^2.0.0",
         "@types/uuid": "^3.4.10",
         "@types/valid-url": "^1.0.3",
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 4286da2e2..f58d8e937 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -51,8 +51,8 @@ interface CSVFieldColumnProps {
     unobserveHeight: (myref: any) => void;
     //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: )
     editableViewProps: () => any;
-    resizeColumns: (n: number) => void;
-    columnStartXCoords: number[];
+    // resizeColumns: (n: number) => void;
+    // columnStartXCoords: number[];
     PanelWidth: number;
     maxColWidth: number;
     // docsByColumnHeader: Map<string, Doc[]>
@@ -69,12 +69,16 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
             return this.props.maxColWidth;
         }
         const i = this.props.columnHeaders.indexOf(this.props.headingObject);
-        if (i < 0 || i > this.props.columnStartXCoords.length - 1) {
-            return this.props.maxColWidth;
+        if (i >= 0 && this.props.columnHeaders[i].width > 0) {
+            return this.props.columnHeaders[i].width;
         }
-        const endColValue = i == this.props.numGroupColumns - 1 ? this.props.PanelWidth : this.props.columnStartXCoords[i + 1];
-        // TODO make the math work here. 35 is half of 70, which is the current width of the divider
-        return endColValue - this.props.columnStartXCoords[i] - 30;
+        return this.props.maxColWidth;
+        // if (i < 0 || i > this.props.columnStartXCoords.length - 1) {
+        //     return this.props.maxColWidth;
+        // }
+        // const endColValue = i == this.props.numGroupColumns - 1 ? this.props.PanelWidth : this.props.columnStartXCoords[i + 1];
+        // // TODO make the math work here. 35 is half of 70, which is the current width of the divider
+        // return endColValue - this.props.columnStartXCoords[i] - 30;
     }
 
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -156,6 +160,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
             this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset'));
             columnHeaders.splice(index, 1);
         }
+        // probably need to add something here for sizing purposes
     };
 
     menuCallback = (x: number, y: number) => {
-- 
cgit v1.2.3-70-g09d2


From 6f7a94b0fa65729f03b49e8aecf9eb3c8a2461ff Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Fri, 12 Aug 2022 14:43:41 -0700
Subject: sizing works but undo redo no longer does

---
 .../views/collections/CollectionNoteTakingView.tsx | 40 ++++++++++++++--------
 .../collections/CollectionNoteTakingViewColumn.tsx | 23 ++++++++-----
 2 files changed, 40 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index b3e154dac..0aa1b6839 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -72,7 +72,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         //     return 3 2 1 4 56
         // }
         if (needsUnsetCategory) {
-            columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, this.maxColWdith));
+            columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
         }
         return columnHeaders;
     }
@@ -103,16 +103,20 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @computed get PanelWidth() {
         return this.props.PanelWidth();
     }
-    @computed get maxColWdith() {
+    @computed get maxColWidth() {
         return this.props.PanelWidth() - 2 * this.xMargin;
     }
+    @computed get availableWidth() {
+        const numDividers = this.columnHeaders.length - 1;
+        return this.maxColWidth - numDividers * this.dividerWidth;
+    }
 
     // If the user has not yet created any docs (in another view), this will create a single column. Otherwise,
     // it will adjust according to the
     constructor(props: any) {
         super(props);
         if (this.columnHeaders === undefined) {
-            this.dataDoc.columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField('New Column', undefined, undefined, this.maxColWdith)]);
+            this.dataDoc.columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField('New Column', undefined, undefined, 1)]);
             // add all of the docs that have not been added to a column to this new column
         }
     }
@@ -186,7 +190,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         // this._disposers.headers = reaction(
         //     () => this.columnHeaders.slice(),
         //     // TODO: is this correct?
-        //     headers => this.resizeColumns(headers.length, true),
+        //     headers => this.resizeColumns(headers.length, false),
         //     { fireImmediately: true }
         // );
     }
@@ -334,7 +338,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
         const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
         const existingWidth = existingHeader?.width ? existingHeader.width : 0;
-        const maxWidth = existingWidth > 0 ? existingWidth : this.maxColWdith - 2 * this.xMargin;
+        const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth - 2 * this.xMargin : this.maxColWidth - 2 * this.xMargin;
         // const index = existingHeader ? this.columnHeaders.indexOf(existingHeader) : 0;
         // const endColValue = index === this.columnHeaders.length - 1 || index > this.columnStartXCoords.length - 1 ? this.PanelWidth : this.columnStartXCoords[index + 1];
         // const maxWidth = index > this.columnStartXCoords.length - 1 ? this.PanelWidth : endColValue - this.columnStartXCoords[index] - 3 * this.xMargin;
@@ -367,10 +371,14 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     // called when a column is either added or deleted. This function creates n evenly spaced columns
     @action
     resizeColumns = (n: number, isAdd: boolean) => {
-        let scaleFactor = isAdd ? Math.floor((n - 1)) / n : Math.floor((n + 1) / n);
-        if (isAdd && n == 1) scaleFactor = 1;
+        if (n == 1) {
+          this.columnHeaders[0].setWidth(1);
+          return;
+        }
+        let scaleFactor = isAdd ? (n - 1) / n : (n + 1) / n;
+        // if (isAdd && n == 1) scaleFactor = 1;
         this.columnHeaders.forEach(h => {
-          h.width < 0 ? h.setWidth((this.maxColWdith - this.dividerWidth) / n) : h.setWidth(h.width * scaleFactor);
+            h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
         });
         // if we're adding, need to
 
@@ -424,7 +432,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         let colStartXCoord = 0;
         for (let i = 0; i < numColumns; i++) {
             coords.push(colStartXCoord);
-            colStartXCoord += this.columnHeaders[i].width + this.dividerWidth;
+            colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth;
         }
 
         coords.push(this.PanelWidth);
@@ -600,13 +608,15 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
                 columnHeaders={this.columnHeaders}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
-                // resizeColumns={this.resizeColumns}
+                resizeColumns={this.resizeColumns}
                 renderChildren={this.children}
                 numGroupColumns={this.numGroupColumns}
                 gridGap={this.gridGap}
                 pivotField={this.notetakingCategoryField}
                 // columnStartXCoords={this.columnStartXCoords}
-                maxColWidth={this.maxColWdith}
+                dividerWidth={this.dividerWidth}
+                maxColWidth={this.maxColWidth}
+                availableWidth={this.availableWidth}
                 PanelWidth={this.PanelWidth}
                 key={heading?.heading ?? ''}
                 headings={this.headings}
@@ -628,9 +638,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @undoBatch
     addGroup = (value: string) => {
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
-        const colWidth = (this.PanelWidth - this.numGroupColumns * this.dividerWidth) / (this.numGroupColumns + 1);
+        // const colWidth = (this.PanelWidth - this.numGroupColumns * this.dividerWidth) / (this.numGroupColumns + 1);
         this.resizeColumns(this.numGroupColumns + 1, true);
-        return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, colWidth)) ? true : false;
+        return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, 1 / (this.numGroupColumns + 1))) ? true : false;
     };
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
@@ -656,8 +666,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     setColumnStartXCoords = (movementX: number, colIndex: number) => {
         const leftHeader = this.columnHeaders[colIndex];
         const rightHeader = this.columnHeaders[colIndex + 1];
-        leftHeader.setWidth(leftHeader.width + movementX);
-        rightHeader.setWidth(rightHeader.width - movementX);
+        leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
+        rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
         // const coords = [...this.columnStartXCoords];
         // coords[colIndex] += movementX;
         // this.columnStartXCoords = coords;
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index f58d8e937..8ad684cec 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -51,10 +51,12 @@ interface CSVFieldColumnProps {
     unobserveHeight: (myref: any) => void;
     //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: )
     editableViewProps: () => any;
-    // resizeColumns: (n: number) => void;
+    resizeColumns: (n: number, isAdd: boolean) => void;
     // columnStartXCoords: number[];
     PanelWidth: number;
     maxColWidth: number;
+    dividerWidth: number;
+    availableWidth: number;
     // docsByColumnHeader: Map<string, Doc[]>
     // setDocsForColHeader: (key: string, docs: Doc[]) => void
 }
@@ -63,16 +65,22 @@ interface CSVFieldColumnProps {
 export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColumnProps> {
     @observable private _background = 'inherit';
 
+    // the "width" property of headers is relative (a percentage of available space)
     @computed get columnWidth() {
         // base cases
-        if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length == 1) {
+        if (!this.props.columnHeaders || !this.props.headingObject) {
             return this.props.maxColWidth;
         }
-        const i = this.props.columnHeaders.indexOf(this.props.headingObject);
-        if (i >= 0 && this.props.columnHeaders[i].width > 0) {
-            return this.props.columnHeaders[i].width;
+        if (this.props.columnHeaders.length == 1) {
+            // this.props.columnHeaders[0].setWidth(1);
+            return this.props.maxColWidth;
         }
-        return this.props.maxColWidth;
+        const i = this.props.columnHeaders.indexOf(this.props.headingObject);
+        return this.props.columnHeaders[i].width * this.props.availableWidth;
+        // if (i >= 0 && this.props.columnHeaders[i].width > 0) {
+        //     return this.props.columnHeaders[i].width;
+        // }
+        // return this.props.maxColWidth;
         // if (i < 0 || i > this.props.columnStartXCoords.length - 1) {
         //     return this.props.maxColWidth;
         // }
@@ -150,7 +158,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         return this.props.addDocument?.(newDoc) || false;
     };
 
-    @undoBatch
     @action
     @undoBatch
     deleteColumn = () => {
@@ -158,9 +165,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         if (columnHeaders && this.props.headingObject) {
             const index = columnHeaders.indexOf(this.props.headingObject);
             this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset'));
+            this.props.resizeColumns(columnHeaders.length - 1, false)
             columnHeaders.splice(index, 1);
         }
-        // probably need to add something here for sizing purposes
     };
 
     menuCallback = (x: number, y: number) => {
-- 
cgit v1.2.3-70-g09d2


From 5655589d46c01af74c959c2d365d3eeb15feb407 Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Fri, 19 Aug 2022 06:48:46 -0500
Subject: persistent resizing undo/redo issues

---
 .../views/collections/CollectionNoteTakingView.tsx | 30 ++++++++++++++--------
 .../collections/CollectionNoteTakingViewColumn.tsx |  6 +++--
 2 files changed, 23 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 0aa1b6839..447e1f0c8 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -368,18 +368,27 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
-    // called when a column is either added or deleted. This function creates n evenly spaced columns
+    // called when a column is either added or deleted.
+    // TODO: this needs help still
     @action
-    resizeColumns = (n: number, isAdd: boolean) => {
+    resizeColumns = (isAdd: boolean, colWidth: number, colIndex: number) => {
+        const n = this.columnHeaders.length;
         if (n == 1) {
-          this.columnHeaders[0].setWidth(1);
-          return;
+            this.columnHeaders[0].setWidth(1);
+            return true;
         }
-        let scaleFactor = isAdd ? (n - 1) / n : (n + 1) / n;
-        // if (isAdd && n == 1) scaleFactor = 1;
-        this.columnHeaders.forEach(h => {
-            h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
+        const scaleFactor = isAdd ? 1 - colWidth : 1 / (1 - colWidth);
+        this.columnHeaders.forEach((h, i) => {
+            if (!(isAdd && i == colIndex)) {
+                h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
+            }
         });
+        return true;
+        // let scaleFactor = isAdd ? (n - 1) / n : (n + 1) / n;
+        // // if (isAdd && n == 1) scaleFactor = 1;
+        // this.columnHeaders.forEach(h => {
+        //     h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
+        // });
         // if we're adding, need to
 
         // const newColXCoords: number[] = [];
@@ -638,9 +647,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @undoBatch
     addGroup = (value: string) => {
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
-        // const colWidth = (this.PanelWidth - this.numGroupColumns * this.dividerWidth) / (this.numGroupColumns + 1);
-        this.resizeColumns(this.numGroupColumns + 1, true);
-        return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, 1 / (this.numGroupColumns + 1))) ? true : false;
+        const newColWidth = 1 / (this.numGroupColumns + 1);
+        return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1) ? true : false;
     };
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 8ad684cec..11a0e69ac 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -51,7 +51,7 @@ interface CSVFieldColumnProps {
     unobserveHeight: (myref: any) => void;
     //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: )
     editableViewProps: () => any;
-    resizeColumns: (n: number, isAdd: boolean) => void;
+    resizeColumns: (isAdd: boolean, colWidth: number, colIndex: number) => boolean;
     // columnStartXCoords: number[];
     PanelWidth: number;
     maxColWidth: number;
@@ -165,8 +165,10 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         if (columnHeaders && this.props.headingObject) {
             const index = columnHeaders.indexOf(this.props.headingObject);
             this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset'));
-            this.props.resizeColumns(columnHeaders.length - 1, false)
+            // should never be 0, currently placeholder
+            const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
             columnHeaders.splice(index, 1);
+            this.props.resizeColumns(false, colWidth, index);
         }
     };
 
-- 
cgit v1.2.3-70-g09d2


From b7c2e1081e4efe7167a86fb1d9f641bea8b93bba Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Mon, 22 Aug 2022 05:51:13 -0500
Subject: commented all NoteTakingView files

---
 .../views/collections/CollectionNoteTakingView.tsx | 17 ++++++----
 .../collections/CollectionNoteTakingViewColumn.tsx | 38 ++++++----------------
 .../CollectionNoteTakingViewDivider.tsx            |  6 +++-
 3 files changed, 25 insertions(+), 36 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index a2f05c031..9e40356d6 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -55,7 +55,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @computed get chromeHidden() {
         return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
     }
-    // columnHeaders() returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
+    // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
     @computed get columnHeaders() {
         const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
         const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset'));
@@ -64,19 +64,17 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         }
         return columnHeaders;
     }
-    // 
+    // notetakingCategoryField returns the key to accessing a document's column value
     @computed get notetakingCategoryField() {
         return 'NotetakingCategory';
     }
-    @computed get filteredChildren() {
-        return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
-    }
     @computed get headerMargin() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
     }
     @computed get xMargin() {
         return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
     }
+    // dividerWidth returns the width of a CollectionNoteTakingViewDivider
     @computed get dividerWidth() {
         return 32;
     }
@@ -86,15 +84,20 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @computed get gridGap() {
         return NumCast(this.layoutDoc._gridGap, 10);
     }
+    // numGroupColumns returns the number of columns
     @computed get numGroupColumns() {
         return this.columnHeaders.length;
     }
+    // PanelWidth returns the size of the total available space the view occupies
     @computed get PanelWidth() {
         return this.props.PanelWidth();
     }
+    // maxColWidth returns the maximum column width, which is slightly less than the total available space.
     @computed get maxColWidth() {
         return this.props.PanelWidth() - 2 * this.xMargin;
     }
+    // availableWidth is the total amount of non-divider width. Since widths are stored relatively, 
+    // we use availableWidth to convert from a percentage to a pixel count.
     @computed get availableWidth() {
         const numDividers = this.columnHeaders.length - 1;
         return this.maxColWidth - numDividers * this.dividerWidth;
@@ -109,7 +112,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         }
     }
 
-    // passed as a prop to the NoteTakingField, which uses this function
+    // children is passed as a prop to the NoteTakingField, which uses this function
     // to render the docs you see within an individual column.
     children = (docs: Doc[]) => {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 11a0e69ac..a866373a9 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -5,11 +5,12 @@ import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { ScriptField } from '../../../fields/ScriptField';
+import { Cast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
+import { returnEmptyString } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
@@ -21,13 +22,10 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionNoteTakingView.scss';
-import { listSpec } from '../../../fields/Schema';
-import { Cast } from '../../../fields/Types';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-// So this is how we are storing a column
 interface CSVFieldColumnProps {
     Document: Doc;
     DataDoc: Opt<Doc>;
@@ -38,7 +36,6 @@ interface CSVFieldColumnProps {
     columnHeaders: SchemaHeaderField[] | undefined;
     headingObject: SchemaHeaderField | undefined;
     yMargin: number;
-    // columnWidth: number;
     numGroupColumns: number;
     gridGap: number;
     type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
@@ -49,44 +46,32 @@ interface CSVFieldColumnProps {
     screenToLocalTransform: () => Transform;
     observeHeight: (myref: any) => void;
     unobserveHeight: (myref: any) => void;
-    //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: )
     editableViewProps: () => any;
     resizeColumns: (isAdd: boolean, colWidth: number, colIndex: number) => boolean;
-    // columnStartXCoords: number[];
     PanelWidth: number;
     maxColWidth: number;
     dividerWidth: number;
     availableWidth: number;
-    // docsByColumnHeader: Map<string, Doc[]>
-    // setDocsForColHeader: (key: string, docs: Doc[]) => void
 }
 
+/**
+ * CollectionNoteTakingViewColumn represents an individual column rendered in CollectionNoteTakingView. The
+ * majority of functions here are for rendering styles.
+ */
 @observer
 export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColumnProps> {
     @observable private _background = 'inherit';
 
-    // the "width" property of headers is relative (a percentage of available space)
+    // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
-        // base cases
         if (!this.props.columnHeaders || !this.props.headingObject) {
             return this.props.maxColWidth;
         }
         if (this.props.columnHeaders.length == 1) {
-            // this.props.columnHeaders[0].setWidth(1);
             return this.props.maxColWidth;
         }
         const i = this.props.columnHeaders.indexOf(this.props.headingObject);
         return this.props.columnHeaders[i].width * this.props.availableWidth;
-        // if (i >= 0 && this.props.columnHeaders[i].width > 0) {
-        //     return this.props.columnHeaders[i].width;
-        // }
-        // return this.props.maxColWidth;
-        // if (i < 0 || i > this.props.columnStartXCoords.length - 1) {
-        //     return this.props.maxColWidth;
-        // }
-        // const endColValue = i == this.props.numGroupColumns - 1 ? this.props.PanelWidth : this.props.columnStartXCoords[i + 1];
-        // // TODO make the math work here. 35 is half of 70, which is the current width of the divider
-        // return endColValue - this.props.columnStartXCoords[i] - 30;
     }
 
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -96,8 +81,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
     _ele: HTMLElement | null = null;
 
-    // This is likely similar to what we will be doing. Why do we need to make these refs?
-    // is that the only way to have drop targets?
     createColumnDropRef = (ele: HTMLDivElement | null) => {
         this.dropDisposer?.();
         if (ele) {
@@ -146,6 +129,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @action pointerLeave = () => (this._background = 'inherit');
     textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true);
 
+    // addNewTextDoc is called when a user starts typing in a column to create a new node
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
@@ -158,6 +142,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         return this.props.addDocument?.(newDoc) || false;
     };
 
+    // deleteColumn is called when a user deletes a column using the 'trash' icon in the button area. 
     @action
     @undoBatch
     deleteColumn = () => {
@@ -270,7 +255,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                 </div>
             </div>
         ) : null;
-        // const templatecols = `${this.props.columnWidth / this.props.numGroupColumns}px `;
         const templatecols = `${this.columnWidth}px `;
         const type = this.props.Document.type;
         return (
@@ -297,7 +281,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                         {!this.props.chromeHidden && type !== DocumentType.PRES ? (
                             <div
                                 className="collectionNoteTakingView-DocumentButtons"
-                                // style={{ width: this.props.columnWidth / this.props.numGroupColumns, marginBottom: 10 }}>
                                 style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
                                 <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
                                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
@@ -326,7 +309,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                 className={'collectionNoteTakingViewFieldColumn' + (SnappingManager.GetIsDragging() ? 'Dragging' : '')}
                 key={heading}
                 style={{
-                    //TODO: change this so that it's based on the column width
                     width: this.columnWidth,
                     background: this._background,
                 }}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 2633bffeb..7defeb031 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,7 +1,6 @@
 import { action, observable } from 'mobx';
 import * as React from 'react';
 import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
-import { Transform } from '../../util/Transform';
 import { UndoManager } from '../../util/UndoManager';
 
 interface DividerProps {
@@ -10,6 +9,11 @@ interface DividerProps {
     setColumnStartXCoords: (movementX: number, colIndex: number) => void;
 }
 
+/**
+ * CollectionNoteTakingViewDivider is a divider between CollectionNoteTakingViewColumns,
+ * which only appear when there is more than 1 column in CollectionNoteTakingView. Dividers
+ * are two simple vertical lines that allow the user to alter the widths of CollectionNoteTakingViewColumns.
+ */
 export class CollectionNoteTakingViewDivider extends React.Component<DividerProps> {
     @observable private isHoverActive = false;
     @observable private isResizingActive = false;
-- 
cgit v1.2.3-70-g09d2


From 3cf60a6118a5994e43c8ac9d00092992d6c83700 Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Mon, 22 Aug 2022 11:03:31 -0400
Subject: can't add column if name in use

---
 src/client/views/collections/CollectionNoteTakingView.tsx  |  9 +++++++--
 .../views/collections/CollectionNoteTakingViewColumn.tsx   | 14 ++++++++------
 .../views/collections/CollectionNoteTakingViewDivider.tsx  |  2 +-
 3 files changed, 16 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 9e40356d6..16fefc55a 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -587,10 +587,15 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
 
     // addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of 
     // columnHeaders and resizing the existing columns to make room for our new one.
-    @undoBatch
     @action
     @undoBatch
     addGroup = (value: string) => {
+        for (const header of this.columnHeaders) {
+          if (header.heading == value) {
+            alert('You cannot use an existing column name. Please try a new column name');
+            return value;
+          }
+        }
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
         const newColWidth = 1 / (this.numGroupColumns + 1);
         return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1) ? true : false;
@@ -607,7 +612,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         }
     };
 
-    // setColumnStartXCoords is used to update column sizes when using the drag handlers between columns
+    // setColumnStartXCoords is used to update column widths when using the drag handlers between columns
     @action
     setColumnStartXCoords = (movementXScreen: number, colIndex: number) => {
         const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0];
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index a866373a9..e619b084b 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -142,15 +142,19 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         return this.props.addDocument?.(newDoc) || false;
     };
 
-    // deleteColumn is called when a user deletes a column using the 'trash' icon in the button area. 
+    // deleteColumn is called when a user deletes a column using the 'trash' icon in the button area.
+    // If the user deletes the first column, the documents get moved to the second column. Otherwise,
+    // all docs are added to the column directly to the left.
     @action
     @undoBatch
     deleteColumn = () => {
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
         if (columnHeaders && this.props.headingObject) {
             const index = columnHeaders.indexOf(this.props.headingObject);
-            this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset'));
-            // should never be 0, currently placeholder
+            const newColIndex = index > 0 ? index - 1 : 1;
+            const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined;
+            const newHeading = newColHeader ? newColHeader.heading : 'unset';
+            this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading));
             const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
             columnHeaders.splice(index, 1);
             this.props.resizeColumns(false, colWidth, index);
@@ -279,9 +283,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                         </div>
 
                         {!this.props.chromeHidden && type !== DocumentType.PRES ? (
-                            <div
-                                className="collectionNoteTakingView-DocumentButtons"
-                                style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
+                            <div className="collectionNoteTakingView-DocumentButtons" style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
                                 <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
                                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
                                 </div>
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 7defeb031..8d659f790 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -10,7 +10,7 @@ interface DividerProps {
 }
 
 /**
- * CollectionNoteTakingViewDivider is a divider between CollectionNoteTakingViewColumns,
+ * CollectionNoteTakingViewDivider are dividers between CollectionNoteTakingViewColumns,
  * which only appear when there is more than 1 column in CollectionNoteTakingView. Dividers
  * are two simple vertical lines that allow the user to alter the widths of CollectionNoteTakingViewColumns.
  */
-- 
cgit v1.2.3-70-g09d2


From 7f4f2840cd64c1eb40851c2ee410fa1905288b73 Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Mon, 22 Aug 2022 15:53:08 -0400
Subject: c

---
 src/client/views/collections/CollectionNoteTakingView.tsx       | 2 +-
 src/client/views/collections/CollectionNoteTakingViewColumn.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 16fefc55a..302c4eaea 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -587,8 +587,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
 
     // addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of 
     // columnHeaders and resizing the existing columns to make room for our new one.
-    @action
     @undoBatch
+    @action
     addGroup = (value: string) => {
         for (const header of this.columnHeaders) {
           if (header.heading == value) {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index e619b084b..4610da4e3 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -145,8 +145,8 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     // deleteColumn is called when a user deletes a column using the 'trash' icon in the button area.
     // If the user deletes the first column, the documents get moved to the second column. Otherwise,
     // all docs are added to the column directly to the left.
-    @action
     @undoBatch
+    @action
     deleteColumn = () => {
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
         if (columnHeaders && this.props.headingObject) {
-- 
cgit v1.2.3-70-g09d2


From 0ed7131587c6739483da64a93d9f2ab6fdfbc15a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 25 Aug 2022 11:06:38 -0400
Subject: fixed crashes in notetaking view and cleaned up code a bit.  fixed
 undo of column deletion.

---
 src/client/util/CurrentUserUtils.ts                |  11 ++-
 .../views/collections/CollectionNoteTakingView.tsx | 103 ++++++++-------------
 .../collections/CollectionNoteTakingViewColumn.tsx |  12 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  18 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  10 +-
 src/client/views/nodes/DocumentView.scss           |   3 +
 src/fields/SchemaHeaderField.ts                    |   2 +-
 src/fields/ScriptField.ts                          |  10 +-
 8 files changed, 79 insertions(+), 90 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 99a8c895f..2321d18ee 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -597,14 +597,17 @@ export class CurrentUserUtils {
     /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
     static setupDockedButtons(doc: Doc, field="myDockedBtns") {
         const dockedBtns = DocCast(doc[field]);
-        const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) =>
+        const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}, funcs?: {[key:string]:string}) =>
             DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
-                            CurrentUserUtils.createToolButton(opts), scripts);
+                            CurrentUserUtils.createToolButton(opts), scripts, funcs);
         
         const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
             { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }},
-            { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }}
-        ];
+            { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }},
+            // { scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, opts: { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,   width: 20}},
+            // { scripts: { onClick:""}, opts:  { title: "Num", icon: "",  toolTip: "Frame Number",  btnType: ButtonType.TextButton,  width: 20}, funcs: { buttonText: 'selectedDocs()?.lastElement()?.currentFrame.toString()'}},
+            // { scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, opts:{title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,  width: 20,}  },
+             ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts = {
             title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index b359ef420..5a6d899ef 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -11,7 +11,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
@@ -19,7 +19,6 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { LightboxView } from '../LightboxView';
-import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -30,13 +29,6 @@ import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivid
 import { CollectionSubView } from './CollectionSubView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
-export type collectionNoteTakingViewProps = {
-    chromeHidden?: boolean;
-    viewType?: CollectionViewType;
-    NativeWidth?: () => number;
-    NativeHeight?: () => number;
-};
-
 /**
  * CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1)
  * add and remove columns (2) change column sizes and (3) move documents within and between columns. This
@@ -45,28 +37,32 @@ export type collectionNoteTakingViewProps = {
  * the rest of Dash, so it may be worthwhile to transition the headers to simple documents.
  */
 @observer
-export class CollectionNoteTakingView extends CollectionSubView<Partial<collectionNoteTakingViewProps>>() {
+export class CollectionNoteTakingView extends CollectionSubView() {
     _disposers: { [key: string]: IReactionDisposer } = {};
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
+    notetakingCategoryField = 'NotetakingCategory';
+    dividerWidth = 16;
     @observable docsDraggedRowCol: number[] = [];
     @observable _cursor: CursorProperty = 'grab';
     @observable _scroll = 0;
     @computed get chromeHidden() {
-        return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+        return BoolCast(this.layoutDoc.chromeHidden);
     }
     // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
     @computed get columnHeaders() {
         const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
-        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset'));
+        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
         if (needsUnsetCategory) {
-            setTimeout(() => columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)));
+            setTimeout(() => {
+                const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
+                if (needsUnsetCategory) {
+                    if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
+                    else this.dataDoc.columnHeaders = new List<SchemaHeaderField>();
+                }
+            });
         }
-        return columnHeaders;
-    }
-    // notetakingCategoryField returns the key to accessing a document's column value
-    @computed get notetakingCategoryField() {
-        return 'NotetakingCategory';
+        return columnHeaders ?? ([] as SchemaHeaderField[]);
     }
     @computed get headerMargin() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
@@ -74,10 +70,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @computed get xMargin() {
         return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
     }
-    // dividerWidth returns the width of a CollectionNoteTakingViewDivider
-    @computed get dividerWidth() {
-        return 32;
-    }
     @computed get yMargin() {
         return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
     }
@@ -99,19 +91,10 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     // availableWidth is the total amount of non-divider width. Since widths are stored relatively,
     // we use availableWidth to convert from a percentage to a pixel count.
     @computed get availableWidth() {
-        const numDividers = this.columnHeaders.length - 1;
+        const numDividers = this.numGroupColumns - 1;
         return this.maxColWidth - numDividers * this.dividerWidth;
     }
 
-    // Documents should NOT have column category fields until entering this view, so the contructor creates the 'New Column'
-    // category for the user to then edit later.
-    constructor(props: any) {
-        super(props);
-        if (this.columnHeaders === undefined) {
-            this.dataDoc.columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField('New Column', undefined, undefined, 1)]);
-        }
-    }
-
     // children is passed as a prop to the NoteTakingField, which uses this function
     // to render the docs you see within an individual column.
     children = (docs: Doc[]) => {
@@ -149,7 +132,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
             }
         });
         // now we add back in the docs that we're dragging
-        if (rowCol.length) {
+        if (rowCol.length && columnHeaders.length > rowCol[1]) {
             const offset = 0;
             sections.get(columnHeaders[rowCol[1]])?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
         }
@@ -236,9 +219,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
             if (this.props.childOpacity) {
                 return this.props.childOpacity();
             }
-            if (this.Document._currentFrame !== undefined) {
-                return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
-            }
         }
         return this.props.styleProvider?.(doc, props, property);
     };
@@ -392,17 +372,19 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
             });
             // we alter the pivot fields of the docs in case they are moved to a new column.
             const colIndex = this.getColumnFromXCoord(xCoord);
-            const colHeader = StrCast(this.columnHeaders[colIndex].heading);
+            const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
             DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader));
             // used to notify sections to re-render
             this.docsDraggedRowCol.length = 0;
-            this.docsDraggedRowCol.push(dropInd, this.getColumnFromXCoord(xCoord));
+            const columnFromCoord = this.getColumnFromXCoord(xCoord);
+            columnFromCoord !== undefined && this.docsDraggedRowCol.push(dropInd, columnFromCoord);
         }
     };
 
     // getColumnFromXCoord returns the column index for a given x-coordinate (currently always the client's mouse coordinate).
     // This function is used to know which document a column SHOULD be in while it is being dragged.
-    getColumnFromXCoord = (xCoord: number): number => {
+    getColumnFromXCoord = (xCoord: number): number | undefined => {
+        let colIndex: number | undefined = undefined;
         const numColumns = this.columnHeaders.length;
         const coords = [];
         let colStartXCoord = 0;
@@ -411,7 +393,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
             colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth;
         }
         coords.push(this.PanelWidth);
-        let colIndex = 0;
         for (let i = 0; i < numColumns; i++) {
             if (xCoord > coords[i] && xCoord < coords[i + 1]) {
                 colIndex = i;
@@ -423,20 +404,16 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
 
     // getDocsFromXCoord returns the docs of a column based on the x-coordinate provided.
     getDocsFromXCoord = (xCoord: number): Doc[] => {
-        const colIndex = this.getColumnFromXCoord(xCoord);
-        const colHeader = StrCast(this.columnHeaders[colIndex].heading);
-        // const docs = this.childDocList
-        const docs = this.childDocs;
         const docsMatchingHeader: Doc[] = [];
-        if (docs) {
-            docs.map(d => {
-                if (d instanceof Promise) return;
-                const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
-                if (sectionValue.toString() == colHeader) {
-                    docsMatchingHeader.push(d);
-                }
-            });
-        }
+        const colIndex = this.getColumnFromXCoord(xCoord);
+        const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+        this.childDocs?.map(d => {
+            if (d instanceof Promise) return;
+            const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
+            if (sectionValue.toString() == colHeader) {
+                docsMatchingHeader.push(d);
+            }
+        });
         return docsMatchingHeader;
     };
 
@@ -511,7 +488,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
                     this.onPointerMove(true, e.clientX, e.clientY);
                     docus?.map((doc: Doc) => this.addDocument(doc));
                     const newDoc = this.childDocs.lastElement();
-                    const colHeader = StrCast(this.columnHeaders[colInd].heading);
+                    const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading);
                     newDoc[this.notetakingCategoryField] = colHeader;
                     const docs = this.childDocList;
                     if (docs && targInd !== -1) {
@@ -570,9 +547,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
                 maxColWidth={this.maxColWidth}
                 availableWidth={this.availableWidth}
                 PanelWidth={this.PanelWidth}
-                key={heading?.heading ?? ''}
+                key={heading?.heading ?? 'unset'}
                 headings={this.headings}
-                heading={heading?.heading ?? ''}
+                heading={heading?.heading ?? 'unset'}
                 headingObject={heading}
                 docList={docList}
                 yMargin={this.yMargin}
@@ -589,10 +566,12 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @undoBatch
     @action
     addGroup = (value: string) => {
-        for (const header of this.columnHeaders) {
-            if (header.heading == value) {
-                alert('You cannot use an existing column name. Please try a new column name');
-                return value;
+        if (this.columnHeaders) {
+            for (const header of this.columnHeaders) {
+                if (header.heading == value) {
+                    alert('You cannot use an existing column name. Please try a new column name');
+                    return value;
+                }
             }
         }
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
@@ -678,10 +657,10 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     }
 
     @computed get nativeWidth() {
-        return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+        return Doc.NativeWidth(this.layoutDoc);
     }
     @computed get nativeHeight() {
-        return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+        return Doc.NativeHeight(this.layoutDoc);
     }
 
     @computed get scaling() {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 4610da4e3..c57677683 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
 import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -22,6 +22,8 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionNoteTakingView.scss';
+import { List } from '../../../fields/List';
+import { Field } from '../../util/ProsemirrorCopy/prompt';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -148,15 +150,17 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @undoBatch
     @action
     deleteColumn = () => {
-        const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
-        if (columnHeaders && this.props.headingObject) {
-            const index = columnHeaders.indexOf(this.props.headingObject);
+        const acolumnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
+        if (acolumnHeaders && this.props.headingObject) {
+            const index = acolumnHeaders.indexOf(this.props.headingObject);
+            const columnHeaders = new List<SchemaHeaderField>(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly.  otherwise we end up changing field values in the undo stack since they are shared by reference
             const newColIndex = index > 0 ? index - 1 : 1;
             const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined;
             const newHeading = newColHeader ? newColHeader.heading : 'unset';
             this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading));
             const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
             columnHeaders.splice(index, 1);
+            Doc.GetProto(this.props.Document).columnHeaders = columnHeaders;
             this.props.resizeColumns(false, colWidth, index);
         }
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 052cbd3bb..c44b33ed0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,6 @@ import { PresBox } from '../../nodes/trails/PresBox';
 import { VideoBox } from '../../nodes/VideoBox';
 import { CreateImage } from '../../nodes/WebBoxRenderer';
 import { StyleProp } from '../../StyleProvider';
-import { CollectionDockingView } from '../CollectionDockingView';
 import { CollectionSubView } from '../CollectionSubView';
 import { TreeViewType } from '../CollectionTreeView';
 import { TabDocView } from '../TabDocView';
@@ -233,11 +232,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const newBoxes = newBox instanceof Doc ? [newBox] : newBox;
             for (const newBox of newBoxes) {
                 if (newBox.activeFrame !== undefined) {
-                    const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field]);
-                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]);
-                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]);
+                    const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]);
+                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}-indexed`]);
+                    CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]);
                     delete newBox.activeFrame;
-                    CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== 'opacity' && (newBox[field] = vals[i]));
+                    CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i]));
                 }
             }
             if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
@@ -275,10 +274,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const layoutDoc = Doc.Layout(d);
             if (this.Document._currentFrame !== undefined) {
                 CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
-                const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
-                vals.x = x + NumCast(vals.x) - dropPos[0];
-                vals.y = y + NumCast(vals.y) - dropPos[1];
-                vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
+                const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
+                const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000), false); // get non-default values for everything else
+                vals.x = x + NumCast(pvals.x) - dropPos[0];
+                vals.y = y + NumCast(pvals.y) - dropPos[1];
+                vals._scrollTop = this.Document.editScrollProgressivize ? pvals._scrollTop : undefined;
                 CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
             } else {
                 d.x = x + NumCast(d.x) - dropPos[0];
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 86566ac6a..efaecb8d2 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -36,7 +36,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
-    public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames
+    public static animFields: { key: string; val?: number }[] = [{ key: '_height' }, { key: '_width' }, { key: 'x' }, { key: 'y' }, { key: '_scrollTop' }, { key: 'opacity', val: 1 }, { key: 'viewScale', val: 1 }, { key: 'panX' }, { key: 'panY' }]; // fields that are configured to be animatable using animation frames
     public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames
     public static animDataFields = ['data', 'text']; // fields that are configured to be animatable using animation frames
     @observable _animPos: number[] | undefined = undefined;
@@ -88,9 +88,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return this.props.styleProvider?.(doc, props, property);
     };
 
-    public static getValues(doc: Doc, time: number) {
+    public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
         return CollectionFreeFormDocumentView.animFields.reduce((p, val) => {
-            p[val] = Cast(`${val}-indexed`, listSpec('number'), [NumCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
+            p[val.key] = Cast(`${val}-indexed`, listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
             return p;
         }, {} as { [val: string]: Opt<number> });
     }
@@ -117,7 +117,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             action(doc => {
                 doc._viewTransition = doc.dataTransition = 'all 1s';
                 CollectionFreeFormDocumentView.animFields.forEach(val => {
-                    const findexed = Cast(doc[`${val}-indexed`], listSpec('number'), null);
+                    const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null);
                     findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
                 });
                 CollectionFreeFormDocumentView.animStringFields.forEach(val => {
@@ -174,7 +174,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in
                 doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)));
             }
-            CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedNumber(val, 'activeFrame', doc, currTimecode)));
+            CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val)));
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
             CollectionFreeFormDocumentView.animDataFields.forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
             const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 9aaaf1e68..ab7116150 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -2,6 +2,7 @@
 
 .documentView-effectsWrapper {
     border-radius: inherit;
+    transition: inherit;
 }
 
 // documentViews have a docView-hack tag which is replaced by this tag when capturing bitmaps (when the dom is converted to an html string)
@@ -212,10 +213,12 @@
     display: flex;
     width: 100%;
     height: 100%;
+    transition: inherit;
 
     .contentFittingDocumentView-previewDoc {
         position: relative;
         display: inline;
+        transition: inherit;
     }
 
     .contentFittingDocumentView-input {
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 1321bc327..0b51db70b 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -115,7 +115,7 @@ export class SchemaHeaderField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return `header(${this.heading},${this.type}})`;
+        return `header(${this.heading},${this.type},${this.width}})`;
     }
     [ToString]() {
         return `SchemaHeaderField`;
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 48d5c5563..d38a019b3 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -189,13 +189,13 @@ export class ComputedField extends ScriptField {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
-    public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
+    public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
         if (!doc[`${fieldKey}-indexed`]) {
             const flist = new List<number>(numberRange(curTimecode + 1).map(i => undefined) as any as number[]);
-            flist[curTimecode] = NumCast(doc[fieldKey]);
+            flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
             doc[`${fieldKey}-indexed`] = flist;
         }
-        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
+        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, ${defaultVal})`, {}, true, {});
         const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {});
         return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
@@ -260,8 +260,8 @@ ScriptingGlobals.add(
 );
 
 ScriptingGlobals.add(
-    function getIndexVal(list: any[], index: number) {
-        return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), undefined as any);
+    function getIndexVal(list: any[], index: number, defaultVal: Opt<number> = undefined) {
+        return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), defaultVal);
     },
     'returns the value at a given index of a list',
     '(list: any[], index: number)'
-- 
cgit v1.2.3-70-g09d2


From ef2cd862c062556008a1897b408399dedaee8210 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 25 Aug 2022 17:04:53 -0400
Subject: fixed issues with reloading notetaking view and having columns be
 exactly the same.  fixed adding columns so that changes are actually
 propagated to the DB.  SchemaHeaderFields are bad news...

---
 .../collections/CollectionNoteTakingView.scss      | 32 +++++---
 .../views/collections/CollectionNoteTakingView.tsx | 42 +++++-----
 .../collections/CollectionNoteTakingViewColumn.tsx | 89 +++++++++-------------
 .../CollectionNoteTakingViewDivider.tsx            |  1 -
 4 files changed, 80 insertions(+), 84 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index fe98f307e..5582fd391 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -1,7 +1,7 @@
 @import '../global/globalCssVariables';
 
 .collectionNoteTakingView-DocumentButtons {
-    display: flex;
+    display: none;
     justify-content: space-between;
     margin: auto;
 }
@@ -51,6 +51,17 @@
     display: flex;
 }
 
+.collectionNoteTakingViewFieldColumn {
+    height: 100%;
+    display: flex;
+    flex-direction: colum;
+}
+.collectionNoteTakingViewFieldColumn:hover {
+    .collectionNoteTakingView-DocumentButtons {
+        display: flex;
+    }
+}
+
 // TODO:glr Turn this into a seperate class
 .documentButtonMenu {
     position: relative;
@@ -82,7 +93,6 @@
     top: 0;
     overflow-y: auto;
     overflow-x: hidden;
-    flex-wrap: wrap;
     transition: top 0.5s;
 
     > div {
@@ -133,6 +143,16 @@
         margin-left: -5;
     }
 
+    .collectionNoteTakingView-sectionDelete {
+        display: none;
+        position: absolute;
+        right: 0;
+        width: max-content;
+        height: max-content;
+        top: 10;
+        padding: 2;
+    }
+
     // Documents in NoteTaking view
     .collectionNoteTakingView-columnDoc {
         display: flex;
@@ -347,14 +367,6 @@
                 }
             }
         }
-
-        .collectionNoteTakingView-sectionDelete {
-            position: absolute;
-            right: 25px;
-            top: 0;
-            height: 100%;
-            display: none;
-        }
     }
 
     .collectionNoteTakingView-sectionHeader:hover {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 5a6d899ef..615141485 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -3,7 +3,7 @@ import { CursorProperty } from 'csstype';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -42,7 +42,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     notetakingCategoryField = 'NotetakingCategory';
-    dividerWidth = 16;
+    public DividerWidth = 16;
     @observable docsDraggedRowCol: number[] = [];
     @observable _cursor: CursorProperty = 'grab';
     @observable _scroll = 0;
@@ -53,10 +53,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @computed get columnHeaders() {
         const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
         const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
-        if (needsUnsetCategory) {
+        if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
             setTimeout(() => {
+                const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
                 const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
-                if (needsUnsetCategory) {
+                if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
                     if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
                     else this.dataDoc.columnHeaders = new List<SchemaHeaderField>();
                 }
@@ -68,10 +69,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
     }
     @computed get xMargin() {
-        return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
+        return NumCast(this.layoutDoc._xMargin, 5);
     }
     @computed get yMargin() {
-        return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
+        return NumCast(this.layoutDoc._yMargin, 5);
     }
     @computed get gridGap() {
         return NumCast(this.layoutDoc._gridGap, 10);
@@ -86,13 +87,13 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
     // maxColWidth returns the maximum column width, which is slightly less than the total available space.
     @computed get maxColWidth() {
-        return this.props.PanelWidth() - 2 * this.xMargin;
+        return this.props.PanelWidth();
     }
     // availableWidth is the total amount of non-divider width. Since widths are stored relatively,
     // we use availableWidth to convert from a percentage to a pixel count.
     @computed get availableWidth() {
         const numDividers = this.numGroupColumns - 1;
-        return this.maxColWidth - numDividers * this.dividerWidth;
+        return this.maxColWidth - numDividers * this.DividerWidth;
     }
 
     // children is passed as a prop to the NoteTakingField, which uses this function
@@ -295,12 +296,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
         const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
         const existingWidth = existingHeader?.width ? existingHeader.width : 0;
-        const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth - 2 * this.xMargin : this.maxColWidth - 2 * this.xMargin;
-        if (d.type === DocumentType.RTF) {
-            return maxWidth;
-        }
-        const width = d[WidthSym]();
-        return width < maxWidth ? width : maxWidth;
+        const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
+        const width = d.fitWidth ? maxWidth : d[WidthSym]();
+        return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
     }
 
     // how to get the height of a document. Nothing special here.
@@ -312,8 +310,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
-            // const colWid = this.columnWidth / this.numGroupColumns;
-            // const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
             const docWid = this.getDocWidth(d);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
@@ -390,7 +386,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         let colStartXCoord = 0;
         for (let i = 0; i < numColumns; i++) {
             coords.push(colStartXCoord);
-            colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth;
+            colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth;
         }
         coords.push(this.PanelWidth);
         for (let i = 0; i < numColumns; i++) {
@@ -543,7 +539,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 numGroupColumns={this.numGroupColumns}
                 gridGap={this.gridGap}
                 pivotField={this.notetakingCategoryField}
-                dividerWidth={this.dividerWidth}
+                dividerWidth={this.DividerWidth}
                 maxColWidth={this.maxColWidth}
                 availableWidth={this.availableWidth}
                 PanelWidth={this.PanelWidth}
@@ -576,7 +572,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         }
         const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
         const newColWidth = 1 / (this.numGroupColumns + 1);
-        return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1) ? true : false;
+        value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1);
+        this.dataDoc.columnHeaders = new List<SchemaHeaderField>(columnHeaders.map(header => header[Copy]()));
+        return true;
     };
 
     onContextMenu = (e: React.MouseEvent): void => {
@@ -621,10 +619,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @computed get buttonMenu() {
         const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         if (menuDoc) {
-            const width: number = NumCast(menuDoc._width, 30);
-            const height: number = NumCast(menuDoc._height, 30);
+            const width = NumCast(menuDoc._width, 30);
+            const height = NumCast(menuDoc._height, 30);
             return (
-                <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
+                <div className="buttonMenu-docBtn" style={{ width, height }}>
                     <DocumentView
                         Document={menuDoc}
                         DataDoc={menuDoc}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index c57677683..d951454ff 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -22,11 +22,6 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionNoteTakingView.scss';
-import { List } from '../../../fields/List';
-import { Field } from '../../util/ProsemirrorCopy/prompt';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
 
 interface CSVFieldColumnProps {
     Document: Doc;
@@ -66,19 +61,15 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
-        if (!this.props.columnHeaders || !this.props.headingObject) {
-            return this.props.maxColWidth;
-        }
-        if (this.props.columnHeaders.length == 1) {
-            return this.props.maxColWidth;
-        }
+        if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length === 1) return '100%';
         const i = this.props.columnHeaders.indexOf(this.props.headingObject);
-        return this.props.columnHeaders[i].width * this.props.availableWidth;
+        return this.props.columnHeaders[i].width * 100 + '%';
     }
 
     private dropDisposer?: DragManager.DragDropDisposer;
     private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
 
+    public static ColumnMargin = 10;
     @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
     @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
     _ele: HTMLElement | null = null;
@@ -153,14 +144,14 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         const acolumnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
         if (acolumnHeaders && this.props.headingObject) {
             const index = acolumnHeaders.indexOf(this.props.headingObject);
-            const columnHeaders = new List<SchemaHeaderField>(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly.  otherwise we end up changing field values in the undo stack since they are shared by reference
+            const columnHeaders = acolumnHeaders; // new List<SchemaHeaderField>(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly.  otherwise we end up changing field values in the undo stack since they are shared by reference
             const newColIndex = index > 0 ? index - 1 : 1;
             const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined;
             const newHeading = newColHeader ? newColHeader.heading : 'unset';
             this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading));
             const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
             columnHeaders.splice(index, 1);
-            Doc.GetProto(this.props.Document).columnHeaders = columnHeaders;
+            //Doc.GetProto(this.props.Document).columnHeaders = columnHeaders;
             this.props.resizeColumns(false, colWidth, index);
         }
     };
@@ -251,9 +242,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                 ref={this._headerRef}
                 style={{
                     marginTop: 2 * this.props.yMargin,
-                    // width: (this.props.columnWidth) /
-                    //     ((uniqueHeadings.length) || 1)
-                    width: this.columnWidth - 20,
+                    width: 'calc(100% - 5px)',
                 }}>
                 <div
                     className="collectionNoteTakingView-sectionHeader-subCont"
@@ -261,48 +250,46 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                     style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
                     <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
                 </div>
+                {(this.props.columnHeaders?.length ?? 0) > 1 && (
+                    <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
+                        <FontAwesomeIcon icon="trash" size="lg" />
+                    </button>
+                )}
             </div>
         ) : null;
-        const templatecols = `${this.columnWidth}px `;
+        const templatecols = this.columnWidth;
         const type = this.props.Document.type;
         return (
             <>
                 {headingView}
-                {
-                    <div style={{ height: '100%' }}>
-                        <div
-                            key={`${heading}-stack`}
-                            className={`collectionNoteTakingView-Nodes`}
-                            style={{
-                                padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
-                                margin: 'auto',
-                                width: 'max-content', //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
-                                height: 'max-content',
-                                position: 'relative',
-                                gridGap: this.props.gridGap,
-                                gridTemplateColumns: templatecols,
-                                gridAutoRows: '0px',
-                            }}>
-                            {this.props.renderChildren(this.props.docList)}
-                        </div>
+                <div style={{ height: '100%' }}>
+                    <div
+                        key={`${heading}-stack`}
+                        className={`collectionNoteTakingView-Nodes`}
+                        style={{
+                            padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
+                            margin: 'auto',
+                            width: 'max-content',
+                            height: 'max-content',
+                            position: 'relative',
+                            gridGap: this.props.gridGap,
+                            gridTemplateColumns: templatecols,
+                            gridAutoRows: '0px',
+                        }}>
+                        {this.props.renderChildren(this.props.docList)}
+                    </div>
 
-                        {!this.props.chromeHidden && type !== DocumentType.PRES ? (
-                            <div className="collectionNoteTakingView-DocumentButtons" style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
-                                <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
-                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
-                                </div>
-                                <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
-                                    <EditableView {...this.props.editableViewProps()} />
-                                </div>
-                                {this.props.columnHeaders?.length && this.props.columnHeaders.length > 1 && (
-                                    <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
-                                        <FontAwesomeIcon icon="trash" size="lg" />
-                                    </button>
-                                )}
+                    {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+                        <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
+                            <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
+                                <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
                             </div>
-                        ) : null}
-                    </div>
-                }
+                            <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
+                                <EditableView {...this.props.editableViewProps()} />
+                            </div>
+                        </div>
+                    ) : null}
+                </div>
             </>
         );
     }
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 8d659f790..a1309b11f 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -57,7 +57,6 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
                         width: 12,
                         borderRight: '4px solid #282828',
                         borderLeft: '4px solid #282828',
-                        margin: '0px 10px',
                     }}
                 />
             </div>
-- 
cgit v1.2.3-70-g09d2


From dff4b18106261af77d81211e5482a38b19b2a166 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 26 Aug 2022 09:19:15 -0400
Subject: more notetaking cleanup

---
 src/client/views/collections/CollectionNoteTakingView.scss   | 12 +++++++++++-
 .../views/collections/CollectionNoteTakingViewColumn.tsx     |  7 +------
 2 files changed, 12 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 5582fd391..08b13fd50 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -54,7 +54,7 @@
 .collectionNoteTakingViewFieldColumn {
     height: 100%;
     display: flex;
-    flex-direction: colum;
+    overflow: hidden;
 }
 .collectionNoteTakingViewFieldColumn:hover {
     .collectionNoteTakingView-DocumentButtons {
@@ -112,6 +112,11 @@
         height: auto;
     }
 
+    .collectionNoteTakingView-columnStack {
+        height: 100%;
+        width: 100%;
+        display: inline-block;
+    }
     .collectionNoteTakingView-Nodes {
         width: 100%;
         height: 100%;
@@ -123,6 +128,11 @@
         left: 0;
         width: 100%;
         position: absolute;
+        margin: auto;
+        width: max-content;
+        height: max-content;
+        position: relative;
+        grid-auto-rows: 0px;
     }
 
     .collectionNoteTakingView-description {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index d951454ff..84d1c0205 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -262,19 +262,14 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         return (
             <>
                 {headingView}
-                <div style={{ height: '100%' }}>
+                <div className="collectionNoteTakingView-columnStack">
                     <div
                         key={`${heading}-stack`}
                         className={`collectionNoteTakingView-Nodes`}
                         style={{
                             padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
-                            margin: 'auto',
-                            width: 'max-content',
-                            height: 'max-content',
-                            position: 'relative',
                             gridGap: this.props.gridGap,
                             gridTemplateColumns: templatecols,
-                            gridAutoRows: '0px',
                         }}>
                         {this.props.renderChildren(this.props.docList)}
                     </div>
-- 
cgit v1.2.3-70-g09d2


From 862133dc5e45ac56b7d5389d08931a9aaa32301a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Mar 2023 15:23:21 -0500
Subject: fixed notetakingview - docs fitting in columns, resizing columns

---
 .../collections/CollectionNoteTakingView.scss      | 20 ++++---
 .../views/collections/CollectionNoteTakingView.tsx | 35 ++++++-------
 .../collections/CollectionNoteTakingViewColumn.tsx | 61 ++++++++++------------
 3 files changed, 58 insertions(+), 58 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 4d1f18e54..be1800d81 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -51,10 +51,6 @@
     display: flex;
 }
 
-.collectionNoteTakingViewFieldColumn {
-    display: flex;
-    overflow: auto;
-}
 .collectionNoteTakingViewFieldColumn:hover {
     .collectionNoteTakingView-DocumentButtons {
         display: flex;
@@ -98,6 +94,11 @@
         position: relative;
         display: block;
     }
+    .collectionNoteTakingViewFieldColumn {
+        overflow: auto;
+        display: flex;
+        flex-direction: column;
+    }
 
     .collectionSchemaView-previewDoc {
         height: 100%;
@@ -111,14 +112,19 @@
         height: auto;
     }
 
+    .collectionNoteTakingView-columnStackContainer {
+        height: 100%;
+        overflow: auto;
+        width: 100%;
+        display: flex;
+    }
     .collectionNoteTakingView-columnStack {
         height: 100%;
         width: 100%;
-        display: inline-block;
+        display: table-cell;
     }
     .collectionNoteTakingView-Nodes {
         width: 100%;
-        height: 100%;
         position: absolute;
         display: flex;
         flex-direction: column;
@@ -128,7 +134,6 @@
         width: 100%;
         position: absolute;
         margin: auto;
-        width: max-content;
         height: max-content;
         position: relative;
         grid-auto-rows: 0px;
@@ -165,6 +170,7 @@
     // Documents in NoteTaking view
     .collectionNoteTakingView-columnDoc {
         display: flex;
+        width: 100%;
         // margin: auto; // Removed auto so that it is no longer center aligned - this could be something we change
         position: relative;
 
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 26c73438c..edd5ba96c 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -217,6 +217,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
 
+    blockPointerEventsWhenDragging = () => (this.docsDraggedRowCol.length ? 'none' : undefined);
     // getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView)
     getDisplayDoc(doc: Doc, width: () => number) {
         const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
@@ -227,6 +228,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             <DocumentView
                 ref={r => (dref = r || undefined)}
                 Document={doc}
+                pointerEvents={this.blockPointerEventsWhenDragging}
                 DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
@@ -276,7 +278,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // getDocTransform is used to get the coordinates of a document when we go from a view like freeform to columns
     getDocTransform(doc: Doc, dref?: DocumentView) {
         const y = this._scroll; // required for document decorations to update when the text box container is scrolled
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+        const { translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
         // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
         return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
     }
@@ -319,18 +321,16 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // Removing example: column widths are [0.5, 0.30, 0.20] --> user deletes the final column --> column widths are [0.625, 0.375].
     // Adding example: column widths are [0.6, 0.4] --> user adds column at end --> column widths are [0.4, 0.267, 0.33]
     @action
-    resizeColumns = (isAdd: boolean, colWidth: number, colIndex: number) => {
-        const n = this.columnHeaders.length;
-        if (n == 1) {
-            this.columnHeaders[0].setWidth(1);
-            return true;
-        }
-        const scaleFactor = isAdd ? 1 - colWidth : 1 / (1 - colWidth);
-        this.columnHeaders.forEach((h, i) => {
-            if (!(isAdd && i == colIndex)) {
-                h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
-            }
-        });
+    resizeColumns = (isAdd: boolean, headers: SchemaHeaderField[], colIndex: number) => {
+        const n = headers.length;
+        const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0);
+        const scaleFactor = 1 / curWidths;
+        this.dataDoc.columnHeaders = new List<SchemaHeaderField>(
+            headers.map(h => {
+                h.setWidth(Math.abs(h.width) * scaleFactor);
+                return h;
+            })
+        );
         return true;
     };
 
@@ -533,7 +533,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 dividerWidth={this.DividerWidth}
                 maxColWidth={this.maxColWidth}
                 availableWidth={this.availableWidth}
-                PanelWidth={this.PanelWidth}
                 key={heading?.heading ?? 'unset'}
                 headings={this.headings}
                 heading={heading?.heading ?? 'unset'}
@@ -555,16 +554,16 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     addGroup = (value: string) => {
         if (this.columnHeaders) {
             for (const header of this.columnHeaders) {
-                if (header.heading == value) {
+                if (header.heading === value) {
                     alert('You cannot use an existing column name. Please try a new column name');
                     return value;
                 }
             }
         }
-        const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
+        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
         const newColWidth = 1 / (this.numGroupColumns + 1);
-        value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1);
-        this.dataDoc.columnHeaders = new List<SchemaHeaderField>(columnHeaders.map(header => header[Copy]()));
+        columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
+        value && this.resizeColumns(true, columnHeaders, this.columnHeaders.length - 1);
         return true;
     };
 
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 84d1c0205..ec6a2bec8 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -44,8 +44,7 @@ interface CSVFieldColumnProps {
     observeHeight: (myref: any) => void;
     unobserveHeight: (myref: any) => void;
     editableViewProps: () => any;
-    resizeColumns: (isAdd: boolean, colWidth: number, colIndex: number) => boolean;
-    PanelWidth: number;
+    resizeColumns: (isAdd: boolean, headers: SchemaHeaderField[], colIndex: number) => boolean;
     maxColWidth: number;
     dividerWidth: number;
     availableWidth: number;
@@ -141,18 +140,12 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @undoBatch
     @action
     deleteColumn = () => {
-        const acolumnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
-        if (acolumnHeaders && this.props.headingObject) {
-            const index = acolumnHeaders.indexOf(this.props.headingObject);
-            const columnHeaders = acolumnHeaders; // new List<SchemaHeaderField>(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly.  otherwise we end up changing field values in the undo stack since they are shared by reference
-            const newColIndex = index > 0 ? index - 1 : 1;
-            const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined;
-            const newHeading = newColHeader ? newColHeader.heading : 'unset';
-            this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading));
-            const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
+        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
+        if (this.props.headingObject) {
+            const index = columnHeaders.indexOf(this.props.headingObject);
+            this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
             columnHeaders.splice(index, 1);
-            //Doc.GetProto(this.props.Document).columnHeaders = columnHeaders;
-            this.props.resizeColumns(false, colWidth, index);
+            this.props.resizeColumns(false, columnHeaders, index);
         }
     };
 
@@ -262,28 +255,30 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         return (
             <>
                 {headingView}
-                <div className="collectionNoteTakingView-columnStack">
-                    <div
-                        key={`${heading}-stack`}
-                        className={`collectionNoteTakingView-Nodes`}
-                        style={{
-                            padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
-                            gridGap: this.props.gridGap,
-                            gridTemplateColumns: templatecols,
-                        }}>
-                        {this.props.renderChildren(this.props.docList)}
-                    </div>
+                <div className="collectionNoteTakingView-columnStackContainer">
+                    <div className="collectionNoteTakingView-columnStack">
+                        <div
+                            key={`${heading}-stack`}
+                            className={`collectionNoteTakingView-Nodes`}
+                            style={{
+                                padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
+                                gridGap: this.props.gridGap,
+                                gridTemplateColumns: templatecols,
+                            }}>
+                            {this.props.renderChildren(this.props.docList)}
+                        </div>
 
-                    {!this.props.chromeHidden && type !== DocumentType.PRES ? (
-                        <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
-                            <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
-                                <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
-                            </div>
-                            <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
-                                <EditableView {...this.props.editableViewProps()} />
+                        {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+                            <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
+                                <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
+                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
+                                </div>
+                                <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
+                                    <EditableView {...this.props.editableViewProps()} />
+                                </div>
                             </div>
-                        </div>
-                    ) : null}
+                        ) : null}
+                    </div>
                 </div>
             </>
         );
-- 
cgit v1.2.3-70-g09d2


From 9c29092fc9df29c02cc83885e4ba5645b71867d4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Mar 2023 15:45:03 -0500
Subject: fix to notetakingiview resizing when deleting a column to force an
 unset column

---
 src/client/views/DocumentDecorations.tsx                     |  2 +-
 src/client/views/collections/CollectionNoteTakingView.tsx    | 12 ++++++------
 .../views/collections/CollectionNoteTakingViewColumn.tsx     |  7 +++----
 3 files changed, 10 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index eeef01d17..2982f8a99 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -797,7 +797,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
 
         // Radius constants
-        const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox;
+        const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
         const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
         const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index edd5ba96c..0e1601f35 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -55,11 +55,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
         if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
             setTimeout(() => {
-                const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
+                const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null));
                 const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
-                if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
-                    if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
-                    else this.dataDoc.columnHeaders = new List<SchemaHeaderField>();
+                if (needsUnsetCategory || columnHeaders.length === 0) {
+                    columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
+                    this.resizeColumns(columnHeaders);
                 }
             });
         }
@@ -321,7 +321,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // Removing example: column widths are [0.5, 0.30, 0.20] --> user deletes the final column --> column widths are [0.625, 0.375].
     // Adding example: column widths are [0.6, 0.4] --> user adds column at end --> column widths are [0.4, 0.267, 0.33]
     @action
-    resizeColumns = (isAdd: boolean, headers: SchemaHeaderField[], colIndex: number) => {
+    resizeColumns = (headers: SchemaHeaderField[]) => {
         const n = headers.length;
         const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0);
         const scaleFactor = 1 / curWidths;
@@ -563,7 +563,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
         const newColWidth = 1 / (this.numGroupColumns + 1);
         columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
-        value && this.resizeColumns(true, columnHeaders, this.columnHeaders.length - 1);
+        value && this.resizeColumns(columnHeaders);
         return true;
     };
 
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index ec6a2bec8..829d055e5 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -44,7 +44,7 @@ interface CSVFieldColumnProps {
     observeHeight: (myref: any) => void;
     unobserveHeight: (myref: any) => void;
     editableViewProps: () => any;
-    resizeColumns: (isAdd: boolean, headers: SchemaHeaderField[], colIndex: number) => boolean;
+    resizeColumns: (headers: SchemaHeaderField[]) => boolean;
     maxColWidth: number;
     dividerWidth: number;
     availableWidth: number;
@@ -142,10 +142,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     deleteColumn = () => {
         const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
         if (this.props.headingObject) {
-            const index = columnHeaders.indexOf(this.props.headingObject);
             this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
-            columnHeaders.splice(index, 1);
-            this.props.resizeColumns(false, columnHeaders, index);
+            columnHeaders.splice(columnHeaders.indexOf(this.props.headingObject), 1);
+            this.props.resizeColumns(columnHeaders);
         }
     };
 
-- 
cgit v1.2.3-70-g09d2


From c70a4c82501a318136b04623f92b35461014b179 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 25 Mar 2023 10:10:49 -0400
Subject: fixed notetakingview pointer/wheel events.

---
 src/client/views/collections/CollectionNoteTakingView.tsx           | 1 +
 src/client/views/collections/CollectionNoteTakingViewColumn.tsx     | 3 ++-
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++++-
 3 files changed, 8 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index fbf7db892..121260680 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -515,6 +515,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                         this.observer.observe(ref);
                     }
                 }}
+                select={this.props.select}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
                 columnHeaders={this.columnHeaders}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 829d055e5..621e3d93b 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -37,6 +37,7 @@ interface CSVFieldColumnProps {
     gridGap: number;
     type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
     headings: () => object[];
+    select: (ctrlPressed: boolean) => void;
     renderChildren: (docs: Doc[]) => JSX.Element[];
     addDocument: (doc: Doc | Doc[]) => boolean;
     createDropTarget: (ele: HTMLDivElement) => void;
@@ -240,7 +241,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                     className="collectionNoteTakingView-sectionHeader-subCont"
                     title={evContents === `No Value` ? `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ''}
                     style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
-                    <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
+                    <EditableView GetValue={() => evContents} isEditingCallback={isEditing => isEditing && this.props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
                 </div>
                 {(this.props.columnHeaders?.length ?? 0) > 1 && (
                     <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e7d1eeb90..be4c2a60e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1996,7 +1996,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return (
             <div
                 className="collectionfreeformview-container"
-                ref={this.createDashEventsTarget}
+                ref={r => {
+                    this.createDashEventsTarget(r);
+                    // prevent wheel events from passivly propagating up through containers
+                    r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false });
+                }}
                 onWheel={this.onPointerWheel}
                 onClick={this.onClick}
                 onPointerDown={this.onPointerDown}
-- 
cgit v1.2.3-70-g09d2


From 8ab9236664c561d54d6a41ecb1eb2eaf6064fc0c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Mar 2023 18:21:13 -0400
Subject: changed longPress to always select and to show decorations.  fixed
 single/double-click code and cleaned up behavior timeouts.  fixed pointer
 events for tree view editing titles and using as powerpoint.

---
 src/client/documents/Documents.ts                  |  11 +-
 src/client/util/DragManager.ts                     |  34 ++--
 src/client/views/DocumentDecorations.tsx           |   3 +-
 src/client/views/InkingStroke.tsx                  |   1 -
 src/client/views/MainView.tsx                      |  17 ++
 .../collections/CollectionNoteTakingViewColumn.tsx |   4 +-
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   3 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   6 +-
 src/client/views/nodes/DocumentView.tsx            | 197 ++++++++++-----------
 src/client/views/nodes/KeyValueBox.tsx             |  24 ++-
 src/client/views/nodes/LinkBox.tsx                 |  54 +++---
 src/client/views/nodes/ScriptingBox.tsx            |   4 +-
 src/client/views/nodes/WebBox.tsx                  |   3 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  11 +-
 src/client/views/pdf/AnchorMenu.tsx                |   2 +-
 18 files changed, 201 insertions(+), 183 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0b6d4380d..2bc1b5b1d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -228,7 +228,8 @@ export class DocumentOptions {
     contextMenuScripts?: List<ScriptField>;
     contextMenuLabels?: List<string>;
     contextMenuIcons?: List<string>;
-    allowClickBeforeDoubleClick?: boolean; // whether a click function can fire before the timeout for a double click has expired
+    defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
+    waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
@@ -408,6 +409,7 @@ export namespace Docs {
                         nativeDimModifiable: true,
                         nativeHeightUnfrozen: true,
                         forceReflow: true,
+                        defaultDoubleClick: 'ignore',
                     },
                 },
             ],
@@ -436,7 +438,7 @@ export namespace Docs {
                 DocumentType.WEB,
                 {
                     layout: { view: WebBox, dataField: defaultDataKey },
-                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
+                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
@@ -578,14 +580,14 @@ export namespace Docs {
                 DocumentType.PRES,
                 {
                     layout: { view: PresBox, dataField: defaultDataKey },
-                    options: {},
+                    options: { defaultDoubleClick: 'ignore' },
                 },
             ],
             [
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -983,6 +985,7 @@ export namespace Docs {
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
+            I.defaultDoubleClick = 'click';
             I.creationDate = new DateField();
             I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             //I['acl-Override'] = SharingPermissions.Unset;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c35941ee5..cc116fb46 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,6 +1,6 @@
 import { action, observable, runInAction } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { Doc, Field, Opt } from '../../fields/Doc';
+import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
@@ -207,24 +207,26 @@ export namespace DragManager {
             dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
-                docDragData.droppedDocuments = await Promise.all(
-                    dragData.draggedDocuments.map(async d =>
-                        !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
-                            ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
-                            : docDragData.dropAction === 'alias'
-                            ? Doc.BestAlias(d)
-                            : docDragData.dropAction === 'proto'
-                            ? Doc.GetProto(d)
-                            : docDragData.dropAction === 'copy'
-                            ? (
-                                  await Doc.MakeClone(d)
-                              ).clone
-                            : d
+                docDragData.droppedDocuments = (
+                    await Promise.all(
+                        dragData.draggedDocuments.map(async d =>
+                            !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
+                                ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
+                                : docDragData.dropAction === 'alias'
+                                ? Doc.BestAlias(d)
+                                : docDragData.dropAction === 'proto'
+                                ? Doc.GetProto(d)
+                                : docDragData.dropAction === 'copy'
+                                ? (
+                                      await Doc.MakeClone(d)
+                                  ).clone
+                                : d
+                        )
                     )
-                );
+                ).filter(d => d);
                 !['same', 'proto'].includes(docDragData.dropAction as any) &&
                     docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
-                        const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec('string'), []);
+                        const dragProps = StrListCast(dragData.draggedDocuments[i].removeDropProperties);
                         const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
                         remProps.map(prop => (drop[prop] = undefined));
                     });
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2982f8a99..2d2d3c2f6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -67,8 +67,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         reaction(
             () => SelectionManager.Views().slice(),
             action(docs => {
-                this._showNothing = true;
-                docs.length > 1 && (this._showNothing = false); // show decorations if multiple docs are selected
+                this._showNothing = !DocumentView.LongPress && docs.length === 1; // show decorations if multiple docs are selected or we're long pressing
                 this._editingTitle = false;
             })
         );
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 3861331b5..a085b69a5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -179,7 +179,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                       UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
                   }),
             action((e: PointerEvent, doubleTap: boolean | undefined) => {
-                doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
                 if (doubleTap) {
                     InkStrokeProperties.Instance._controlButton = true;
                     InkStrokeProperties.Instance._currentPoint = -1;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a30d139be..c84d204d5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -214,6 +214,8 @@ export class MainView extends React.Component {
         window.removeEventListener('keyup', KeyManager.Instance.unhandle);
         window.removeEventListener('keydown', KeyManager.Instance.handle);
         window.removeEventListener('pointerdown', this.globalPointerDown, true);
+        window.removeEventListener('pointermove', this.globalPointerMove, true);
+        window.removeEventListener('mouseclick', this.globalPointerClick, true);
         window.removeEventListener('paste', KeyManager.Instance.paste as any);
         document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
@@ -485,7 +487,20 @@ export class MainView extends React.Component {
         );
     }
 
+    private longPressTimer: NodeJS.Timeout | undefined;
+    globalPointerClick = action((e: any) => {
+        this.longPressTimer && clearTimeout(this.longPressTimer);
+        DocumentView.LongPress = false;
+    });
+    globalPointerMove = action((e: PointerEvent) => {
+        if (e.movementX > 3 || e.movementY > 3) this.longPressTimer && clearTimeout(this.longPressTimer);
+    });
     globalPointerDown = action((e: PointerEvent) => {
+        DocumentView.LongPress = false;
+        this.longPressTimer = setTimeout(
+            action(() => (DocumentView.LongPress = true)),
+            1000
+        );
         DocumentManager.removeOverlayViews();
         Doc.linkFollowUnhighlight();
         AudioBox.Enabled = true;
@@ -506,6 +521,8 @@ export class MainView extends React.Component {
         window.addEventListener('dragover', e => e.preventDefault(), false);
         // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
         document.addEventListener('pointerdown', this.globalPointerDown, true);
+        document.addEventListener('pointermove', this.globalPointerMove, true);
+        document.addEventListener('mouseclick', this.globalPointerClick, true);
         document.addEventListener(
             'click',
             (e: MouseEvent) => {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 621e3d93b..28bdd0cb9 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -268,7 +268,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                             {this.props.renderChildren(this.props.docList)}
                         </div>
 
-                        {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+                        {!this.props.chromeHidden ? (
                             <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
                                 <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
                                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
@@ -289,7 +289,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         const heading = this._heading;
         return (
             <div
-                className={'collectionNoteTakingViewFieldColumn' + (SnappingManager.GetIsDragging() ? 'Dragging' : '')}
+                className="collectionNoteTakingViewFieldColumn"
                 key={heading}
                 style={{
                     width: this.columnWidth,
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 631b9add5..5e6849e7c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -422,7 +422,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         this._lastView = this._view;
                     })}
                     renderDepth={0}
-                    LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString('data') : undefined}
+                    LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined}
                     Document={this._document}
                     DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
                     ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index af2d148e0..257428d56 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -305,7 +305,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument('-text-', {
+        const bullet = Docs.Create.TextDocument('', {
             layout: CollectionView.LayoutString('data'),
             title: '-title-',
             treeViewExpandedViewLock: true,
@@ -326,7 +326,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
-        FormattedTextBox.SelectOnLoad = bullet[Id];
+        DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
+
         return bullet;
     }
 
@@ -902,6 +903,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
+                onClickScriptDisable="never"
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index be4c2a60e..8104ab1a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -822,7 +822,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
-        } else if (!e.cancelBubble) {
+        } else {
+            this.props.DocumentView?.().docView?.cancelMoveEvents();
             if (this.tryDragCluster(e, this._hitCluster)) {
                 return true;
             }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index c21bc9754..9e56de8c2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -127,9 +127,9 @@ export class DocumentContentsView extends React.Component<
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         if (!this.layoutDoc) return '<p>awaiting layout</p>';
-        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString('data'));
+        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
         const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
-        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? 'proto' : '');
+        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
         if (typeof layout === 'string') return layout;
         return '<p>Loading layout</p>';
     }
@@ -273,7 +273,7 @@ export class DocumentContentsView extends React.Component<
                 jsx={layoutFrame}
                 showWarnings={true}
                 onError={(test: any) => {
-                    console.log('DocumentContentsView:' + test);
+                    console.log('DocumentContentsView:' + test, bindings, layoutFrame);
                 }}
             />
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 42c2b28ba..c8f677c5f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -124,6 +124,7 @@ export interface DocComponentView {
     select?: (ctrlKey: boolean, shiftKey: boolean) => void;
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
+    onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
@@ -133,6 +134,7 @@ export interface DocComponentView {
     setFocus?: () => void; // sets input focus to the componentView
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
+    fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
     fieldKey?: string;
     annotationKey?: string;
     getTitle?: () => string;
@@ -192,6 +194,7 @@ export interface DocumentViewSharedProps {
     ignoreAutoHeight?: boolean;
     forceAutoHeight?: boolean;
     disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
+    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
     pointerEvents?: () => Opt<string>;
     scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
     createNewFilterDoc?: () => void;
@@ -245,9 +248,10 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
 @observer
 export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
     public static SelectAfterContextMenu = true; //  whether a document should be selected after it's contextmenu is triggered.
-    private _cursorTimer: NodeJS.Timeout | undefined;
-    private _longPress = false;
     private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _doubleClickTimeout: NodeJS.Timeout | undefined;
+    private _singleClickFunc: undefined | (() => any);
+    private _longPressSelector: NodeJS.Timeout | undefined;
     private _downX: number = 0;
     private _downY: number = 0;
     private _downTime: number = 0;
@@ -257,7 +261,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _doubleTap = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _titleRef = React.createRef<EditableView>();
-    private _timeout: NodeJS.Timeout | undefined;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
@@ -265,8 +268,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
     @observable _animateScaleTime: Opt<number>; // milliseconds for animating between views.  defaults to 300 if not uset
     @observable _animateScalingTo = 0;
-    @observable _pendingDoubleClick = false;
-    @observable _cursorPress = false;
 
     private get topMost() {
         return this.props.renderDepth === 0 && !LightboxView.LightboxDoc;
@@ -331,9 +332,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get nativeHeight() {
         return this.props.NativeHeight();
     }
+    @computed get disableClickScriptFunc() {
+        return (
+            DocumentView.LongPress ||
+            this.props.onClickScriptDisable === 'always' ||
+            this._componentView?.onClickScriptDisable?.() ||
+            this._componentView?.isAnyChildContentActive?.()
+        );
+    }
     @computed get onClickHandler() {
-        const noClick = this.props.LayoutTemplateString?.includes(KeyValueBox.name) || this.rootSelected() || this.props.isSelected() || this._componentView?.isAnyChildContentActive?.();
-        return noClick ? undefined : this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+        return this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
     }
     @computed get onDoubleClickHandler() {
         return this.props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
@@ -446,7 +454,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         } else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
-                if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
+                if (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) {
                     this.cleanUpInteractions();
                     this.startDragging(this._downX, this._downY, this.Document.dropAction ? (this.Document.dropAction as any) : e.ctrlKey || e.altKey ? 'alias' : undefined);
                 }
@@ -585,91 +593,76 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             let stopPropagate = true;
             let preventDefault = true;
-            const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
             (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
-            if (this._doubleTap && (![DocumentType.FONTICON, DocumentType.PRES].includes(this.props.Document.type as any) || this.onDoubleClickHandler)) {
-                // && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
-                if (this._timeout) {
-                    clearTimeout(this._timeout);
-                    this._pendingDoubleClick = false;
-                    this._timeout = undefined;
+            if (this._doubleTap) {
+                if (this.onDoubleClickHandler?.script) {
+                    const { clientX, clientY, shiftKey, altKey, ctrlKey } = e; // or we could call e.persist() to capture variables
+                    // prettier-ignore
+                    const func = () => this.onDoubleClickHandler.script.run( {
+                            this: this.layoutDoc,
+                            self: this.rootDoc,
+                            scriptContext: this.props.scriptContext,
+                            thisContainer: this.props.ContainingCollectionDoc,
+                            documentView: this.props.DocumentView(),
+                            clientX, clientY,  altKey,  shiftKey,  ctrlKey,
+                            value: undefined,
+                        }, console.log  );
+                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
+                } else if (!Doc.IsSystem(this.rootDoc) && (this.Document.defaultDoubleClick === undefined || this.Document.defaultDoubleClick === 'default')) {
+                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
+                    SelectionManager.DeselectAll();
+                    Doc.UnBrushDoc(this.props.Document);
+                } else {
+                    this._singleClickFunc?.();
                 }
-                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) {
-                    // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                    const { clientX, clientY, shiftKey, altKey, ctrlKey } = e;
+                this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
+                this._doubleClickTimeout = undefined;
+                this._singleClickFunc = undefined;
+            } else {
+                let clickFunc: undefined | (() => any);
+                if (!this.disableClickScriptFunc && this.onClickHandler?.script) {
+                    const { clientX, clientY, shiftKey, altKey, metaKey } = e;
                     const func = () =>
-                        this.onDoubleClickHandler.script.run(
+                        this.onClickHandler?.script.run(
                             {
                                 this: this.layoutDoc,
                                 self: this.rootDoc,
+                                _readOnly_: false,
                                 scriptContext: this.props.scriptContext,
                                 thisContainer: this.props.ContainingCollectionDoc,
                                 documentView: this.props.DocumentView(),
                                 clientX,
                                 clientY,
-                                altKey,
                                 shiftKey,
-                                ctrlKey,
-                                value: undefined,
+                                altKey,
+                                metaKey,
                             },
                             console.log
-                        );
-                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
-                } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) {
-                    //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
-                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
-                    SelectionManager.DeselectAll();
-                    Doc.UnBrushDoc(this.props.Document);
+                        ).result?.select === true
+                            ? this.props.select(false)
+                            : '';
+                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
+                } else if (!this.disableClickScriptFunc && this.allLinks.length && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+                    clickFunc = () => {
+                        SelectionManager.DeselectAll();
+                        LinkFollower.FollowLink(undefined, this.Document, e.altKey);
+                    };
+                } else {
+                    if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
+                        // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                        stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
+                    }
+                    preventDefault = false;
                 }
-            } else if (!this._longPress && this.onClickHandler?.script && !isScriptBox()) {
-                // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                const { clientX, clientY, shiftKey, altKey, metaKey } = e;
-                const func = () =>
-                    this.onClickHandler?.script.run(
-                        {
-                            this: this.layoutDoc,
-                            self: this.rootDoc,
-                            _readOnly_: false,
-                            scriptContext: this.props.scriptContext,
-                            thisContainer: this.props.ContainingCollectionDoc,
-                            documentView: this.props.DocumentView(),
-                            clientX,
-                            clientY,
-                            shiftKey,
-                            altKey,
-                            metaKey,
-                        },
-                        console.log
-                    ).result?.select === true
-                        ? this.props.select(false)
-                        : '';
-                const clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
-                if (this.onDoubleClickHandler && !this.props.Document.allowClickBeforeDoubleClick) {
-                    runInAction(() => (this._pendingDoubleClick = true));
-                    this._timeout = setTimeout(() => {
-                        this._timeout = undefined;
-                        clickFunc();
-                    }, 150);
-                } else clickFunc();
-            } else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
-                SelectionManager.DeselectAll();
-                this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, e.altKey);
-            } else {
-                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
-                    // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
-                    stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
+
+                this._singleClickFunc = clickFunc ?? (() => (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey));
+                if ((clickFunc && this.Document.waitForDoubleClickToClick !== 'never') || this.Document.waitForDoubleClickToClick === 'always') {
+                    this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
+                    this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300);
                 } else {
-                    runInAction(() => (this._pendingDoubleClick = true));
-                    this._timeout = setTimeout(
-                        action(() => {
-                            this._pendingDoubleClick = false;
-                            this._timeout = undefined;
-                        }),
-                        350
-                    );
-                    (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey);
+                    this._singleClickFunc();
+                    this._singleClickFunc = undefined;
                 }
-                preventDefault = false;
             }
             stopPropagate && e.stopPropagation();
             preventDefault && e.preventDefault();
@@ -678,6 +671,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
+        this._longPressSelector = setTimeout(() => DocumentView.LongPress && this.props.select(false), 1000);
         if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView();
         if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
         // 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)
@@ -689,13 +683,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             return;
         }
-        this._cursorTimer = setTimeout(
-            action(() => {
-                this._cursorPress = true;
-                this.props.select(false);
-            }),
-            1000 // long press required duration
-        );
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._downTime = Date.now();
@@ -719,44 +706,36 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             document.removeEventListener('pointerup', this.onPointerUp);
             document.addEventListener('pointerup', this.onPointerUp);
-        } else {
-            this._cursorTimer && clearTimeout(this._cursorTimer);
-            this._cursorPress = false;
         }
     };
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (e.cancelBubble) return;
+        if (this.layoutDoc._lockedPosition || DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if (((!this.topMost && this.props.isDocumentActive?.()) || this.layoutDoc.onDragStart) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
-                if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
-                    document.removeEventListener('pointermove', this.onPointerMove);
-                    document.removeEventListener('pointerup', this.onPointerUp);
-                    this._cursorTimer && clearTimeout(this._cursorTimer);
-                    this._cursorPress = false;
-                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
-                }
+                this.cleanupPointerEvents();
+                this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
     };
 
+    cancelMoveEvents = () => document.removeEventListener('pointermove', this.onPointerMove);
+
     cleanupPointerEvents = () => {
         this.cleanUpInteractions();
-        document.removeEventListener('pointermove', this.onPointerMove);
+        this.cancelMoveEvents();
         document.removeEventListener('pointerup', this.onPointerUp);
     };
 
     @action
     onPointerUp = (e: PointerEvent): void => {
         this.cleanupPointerEvents();
-        this._longPress = this._cursorPress;
-        this._cursorTimer && clearTimeout(this._cursorTimer);
-        this._cursorPress = false;
+        this._longPressSelector && clearTimeout(this._longPressSelector);
 
         const now = Date.now();
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
@@ -766,6 +745,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             // bcz: this is a placeholder.  documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
             if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
+        if (DocumentView.LongPress) e.preventDefault();
     };
 
     @undoBatch
@@ -1051,7 +1031,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         // Add link to help documentation
         if (documentationDescription && documentationLink) {
-            console.log('add documentation item');
             helpItems.push({
                 description: documentationDescription,
                 event: () => {
@@ -1078,7 +1057,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
-    onClickFunc = () => this.onClickHandler as any as ScriptField; // bcz: typing HACK.  check and fix.
+    onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean) => {
@@ -1101,7 +1080,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
                 .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
         const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
-        return !this.props.isSelected() &&
+        return !this.props.LayoutTemplateString &&
+            !this.props.isSelected() &&
             LightboxView.LightboxDoc !== this.rootDoc &&
             this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
@@ -1129,7 +1109,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </Tooltip>
         );
     }
-    contentPointerEvents = () => (this.onClickHandler ? 'none' : this.pointerEvents);
+    contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
         return (
@@ -1326,7 +1306,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-captionWrapper"
                 style={{
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                    pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
                     maxHeight: `max(100%, ${20 * ffscale()}px)`,
                 }}>
@@ -1360,7 +1340,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button
                     color: lightOrDark(background),
                     background,
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                    pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                 }}>
                 <EditableView
                     ref={this._titleRef}
@@ -1420,7 +1400,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         ...style,
                         background: isButton || thumb ? undefined : this.backgroundColor,
                         opacity: this.opacity,
-                        cursor: this._cursorPress ? 'wait' : Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
+                        cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
                         color: StrCast(this.layoutDoc.color, 'inherit'),
                         fontFamily: StrCast(this.Document._fontFamily, 'inherit'),
                         fontSize: Cast(this.Document._fontSize, 'string', null),
@@ -1428,7 +1408,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this.animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
                     }}>
                     {this.innards}
-                    {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
+                    {!this.disableClickScriptFunc && this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
                     {this.widgetDecorations ?? null}
                 </div>
             )
@@ -1530,6 +1510,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
     public static ROOT_DIV = 'documentView-effectsWrapper';
+    @observable public static LongPress = false;
     public get displayName() {
         return 'DocumentView(' + this.props.Document?.title + ')';
     } // this makes mobx trace() statements more descriptive
@@ -1628,7 +1609,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?.LayoutFieldKey || 'layout';
     }
     get fitWidth() {
-        return this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
+        return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
     }
 
     @computed get hideLinkButton() {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0b1de0ff6..9c31ed3e4 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -3,24 +3,24 @@ import { observer } from 'mobx-react';
 import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { Cast, DocCast, FieldValue, NumCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
 import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
 import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
 import e = require('express');
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { ImageBox } from './ImageBox';
-import { OpenWhere } from './DocumentView';
+import { returnTrue } from '../../../Utils';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -30,8 +30,8 @@ export type KVPScript = {
 
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string) {
-        return FieldView.LayoutString(KeyValueBox, fieldStr);
+    public static LayoutString() {
+        return FieldView.LayoutString(KeyValueBox, 'data');
     }
 
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -39,6 +39,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     private _keyInput = React.createRef<HTMLInputElement>();
     private _valInput = React.createRef<HTMLInputElement>();
 
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
+    onClickScriptDisable: () => 'always' = () => 'always';
+    fitWidth = returnTrue;
+
     @observable private rows: KeyValuePair[] = [];
 
     @computed get splitPercentage() {
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 43f4b43fb..470f7e803 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,29 +1,39 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { emptyFunction, returnFalse } from "../../../Utils";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { StyleProp } from "../StyleProvider";
-import { ComparisonBox } from "./ComparisonBox";
+import React = require('react');
+import { observer } from 'mobx-react';
+import { emptyFunction, returnFalse, returnTrue } from '../../../Utils';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { StyleProp } from '../StyleProvider';
+import { ComparisonBox } from './ComparisonBox';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./LinkBox.scss";
+import './LinkBox.scss';
 
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LinkBox, fieldKey);
+    }
     isContentActiveFunc = () => this.isContentActive();
+
+    onClickScriptDisable: () => 'always' = () => 'always';
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
     render() {
-        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => this.dataDoc.treeViewOpen = true);
-        return <div className={`linkBox-container${this.isContentActive() ? "-interactive" : ""}`}
-            style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} >
-            <ComparisonBox {...this.props}
-                fieldKey="anchor"
-                setHeight={emptyFunction}
-                dontRegisterView={true}
-                renderDepth={this.props.renderDepth + 1}
-                isContentActive={this.isContentActiveFunc}
-                addDocument={returnFalse}
-                removeDocument={returnFalse}
-                moveDocument={returnFalse} />
-        </div>;
+        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => (this.dataDoc.treeViewOpen = true));
+        return (
+            <div className={`linkBox-container${this.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
+                <ComparisonBox
+                    {...this.props}
+                    fieldKey="anchor"
+                    setHeight={emptyFunction}
+                    dontRegisterView={true}
+                    renderDepth={this.props.renderDepth + 1}
+                    isContentActive={this.isContentActiveFunc}
+                    addDocument={returnFalse}
+                    removeDocument={returnFalse}
+                    moveDocument={returnFalse}
+                />
+            </div>
+        );
     }
-} 
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index a6e2c3e90..5683b0fe7 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -8,7 +8,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { returnEmptyString } from '../../../Utils';
+import { returnEmptyString, returnTrue } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { CompileScript, ScriptParam } from '../../util/Scripting';
@@ -114,6 +114,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         }
     }
 
+    onClickScriptDisable: () => 'always' = () => 'always';
+
     @action
     componentDidMount() {
         this.props.setContentView?.(this);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f7425b26a..73283263f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -205,7 +205,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     this._webPageHasBeenRendered = true;
                 } else if (
                     (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
-                    !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc
                 ) {
                     // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
@@ -877,7 +876,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+        const interactive = this.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
             <div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
                 {this.urlContent}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d9364e5b5..468bcc4d8 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -817,7 +817,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
             setMode: () => SetActiveInkWidth(value.toString()),
         }],
         ['strokeColor', {
-            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.color) : ActiveInkColor()),
+            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()),
             setInk: (doc: Doc) => (doc.color = String(value)),
             setMode: () => SetActiveInkColor(StrCast(value)),
         }],
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ddec86606..3ce2366f8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1477,7 +1477,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
         }
     };
-    onSelectMove = (e: PointerEvent) => e.stopPropagation();
+    onSelectMove = (e: PointerEvent) => {
+        this.props.DocumentView?.().docView?.cancelMoveEvents();
+        e.stopPropagation();
+    };
     onSelectEnd = (e: PointerEvent) => {
         document.removeEventListener('pointerup', this.onSelectEnd);
         document.removeEventListener('pointermove', this.onSelectMove);
@@ -1497,10 +1500,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return;
             }
         }
-
-        if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
-            e.stopPropagation();
-        }
     };
     @action
     onDoubleClick = (e: React.MouseEvent): void => {
@@ -1566,8 +1565,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (this.props.isSelected(true)) {
             // if text box is selected, then it consumes all click events
             (e.nativeEvent as any).handledByInnerReactInstance = true;
-            if (this.ProseRef?.children[0] !== e.nativeEvent.target) e.stopPropagation(); // if you double click on text, then it will be selected instead of sending a double click to DocumentView & opening a lightbox.  Also,if a text box has isLinkButton, this will prevent link following if you've selected the document to edit it.
-            // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks (see above comment)
             this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey);
         }
         this._forceUncollapse = !(this._editorView!.root as any).getSelection().isCollapsed;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index fc8f1da49..7392d2706 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -80,7 +80,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             { fireImmediately: true }
         );
         this._disposer = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             selected => {
                 this._showLinkPopup = false;
                 AnchorMenu.Instance.fadeOut(true);
-- 
cgit v1.2.3-70-g09d2

