From 130e26103461f23a2d8b57ea2eef6b17eefb8f8e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 21:46:20 -0400
Subject: updated childDetailedView stuff for pivot view

---
 src/client/views/collections/CollectionTimeView.tsx | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 53de2fbbe..31b720b81 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -5,7 +5,7 @@ import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
 import { Scripting } from "../../util/Scripting";
 import { ContextMenu } from "../ContextMenu";
@@ -19,6 +19,7 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 import React = require("react");
+import { DocumentView } from "../nodes/DocumentView";
 
 @observer
 export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -28,14 +29,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     componentWillUnmount() {
         this.props.Document.onChildClick = undefined;
     }
-    componentDidMount() {
-        const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked;
-        const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(thisContainer.childDetailed, alias, 'layout_detailView'); alias.layoutKey='layout_detailedView'; alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+    async componentDidMount() {
+        const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" });
         this.props.Document._fitToBox = true;
         if (!this.props.Document.onViewDefClick) {
             this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
         }
+        this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked;
+            DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
     }
 
     layoutEngine = () => this._layoutEngine;
-- 
cgit v1.2.3-70-g09d2


From d5c0ed9a29593583a4180973f854106dc10d4b30 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Apr 2020 02:32:47 -0400
Subject: moved filter to right side of collection.  got rid of back button in
 pivot view - click background.

---
 src/Utils.ts                                       | 10 +++--
 .../views/collections/CollectionTimeView.tsx       | 30 +++++++--------
 src/client/views/collections/CollectionView.scss   |  4 +-
 src/client/views/collections/CollectionView.tsx    | 43 +++++++++++++++-------
 .../views/collections/CollectionViewChromes.scss   |  8 +++-
 .../views/collections/CollectionViewChromes.tsx    | 12 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +-
 8 files changed, 69 insertions(+), 43 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index a8cde0624..58f272ba5 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -475,7 +475,9 @@ export function setupMoveUpEvents(
     e: React.PointerEvent,
     moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
     upEvent: (e: PointerEvent) => void,
-    clickEvent: (e: PointerEvent) => void) {
+    clickEvent: (e: PointerEvent) => void,
+    stopPropagation: boolean = true
+) {
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
 
@@ -499,8 +501,10 @@ export function setupMoveUpEvents(
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
     };
-    e.stopPropagation();
-    e.preventDefault();
+    if (stopPropagation) {
+        e.stopPropagation();
+        e.preventDefault();
+    }
     document.removeEventListener("pointermove", _moveEvent);
     document.removeEventListener("pointerup", _upEvent);
     document.addEventListener("pointermove", _moveEvent);
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 31b720b81..1760e2419 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -72,8 +72,22 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         }), returnFalse, emptyFunction);
     }
 
+    contentsDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+            let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
+            if (prevFilterIndex > 0) {
+                prevFilterIndex--;
+                this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
+                this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
+                this.props.Document._prevFilterIndex = prevFilterIndex;
+            } else {
+                this.props.Document._docFilters = new List([]);
+            }
+        }), false);
+    }
+
     @computed get contents() {
-        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }}>
+        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
             <CollectionFreeFormView {...this.props} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
@@ -132,20 +146,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
             color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         };
         return <div className={"pivotKeyEntry"}>
-            <button className="collectionTimeView-backBtn"
-                onClick={action(() => {
-                    let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
-                    if (prevFilterIndex > 0) {
-                        prevFilterIndex--;
-                        this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
-                        this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
-                        this.props.Document._prevFilterIndex = prevFilterIndex;
-                    } else {
-                        this.props.Document._docFilters = new List([]);
-                    }
-                })}>
-                back
-            </button>
             <EditableView {...newEditableViewProps} display={"inline"} menuCallback={this.menuCallback} />
         </div>;
     }
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index a0b73b90f..d43dd387a 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -20,7 +20,7 @@
         top: 55%;
         border: 1px black solid;
         z-index: 2;
-        left: -10px;
+        right: -10px;
     }
     .collectionTimeView-treeView {
         display: flex;
@@ -28,7 +28,7 @@
         width: 200px;
         height: 100%;
         position: absolute;
-        left: 0;
+        right: 0;
         top: 0;
 
         .collectionTimeView-addfacet {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 831691f8c..0a3577bc7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { List } from '../../../new_fields/List';
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
 import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
-import { Utils, setupMoveUpEvents, returnFalse, returnZero } from '../../../Utils';
+import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -45,6 +45,7 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
+import { Transform } from 'prosemirror-transform';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -281,7 +282,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
     set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
     bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0);
     facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth));
 
     @computed get dataDoc() {
@@ -389,7 +389,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
-            this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
+            this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
             return false;
         }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
     }
@@ -415,27 +415,44 @@ export class CollectionView extends Touchable<FieldViewProps> {
                 <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
                         <div className="collectionTimeView-button">
-                            <span className="collectionTimeView-span">Facet Filters</span>
                             <FontAwesomeIcon icon={faEdit} size={"lg"} />
+                            <span className="collectionTimeView-span">Facet Filters</span>
                         </div>
                     </Flyout>
                 </div>
                 <div className="collectionTimeView-tree" key="tree">
-                    <CollectionTreeView {...this.props}
+                    <CollectionTreeView
+                        Document={facetCollection}
+                        DataDoc={facetCollection}
+                        fieldKey={`${this.props.fieldKey}-filter`}
                         CollectionView={this}
-                        treeViewHideTitle={true}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        PanelWidth={this.facetWidth}
+                        PanelHeight={this.props.PanelHeight}
                         NativeHeight={returnZero}
                         NativeWidth={returnZero}
+                        LibraryPath={emptyPath}
+                        rootSelected={this.props.rootSelected}
+                        renderDepth={1}
+                        dropAction={this.props.dropAction}
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        addDocTab={returnFalse}
+                        pinToPres={returnFalse}
+                        isSelected={returnFalse}
+                        select={returnFalse}
+                        bringToFront={emptyFunction}
+                        active={this.props.active}
+                        whenActiveChanged={returnFalse}
+                        treeViewHideTitle={true}
+                        ContentScaling={returnOne}
+                        focus={returnFalse}
                         treeViewHideHeaderFields={true}
                         onCheckedClick={this.scriptField!}
                         ignoreFields={this.ignoreFields}
                         annotationsKey={""}
                         dontRegisterView={true}
-                        PanelWidth={this.facetWidth}
-                        DataDoc={facetCollection}
-                        Document={facetCollection}
                         backgroundColor={this.filterBackground}
-                        fieldKey={`${this.props.fieldKey}-filter`}
                         moveDocument={returnFalse}
                         removeDocument={returnFalse}
                         addDocument={returnFalse} />
@@ -461,7 +478,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             }}
             onContextMenu={this.onContextMenu}>
             {this.showIsTagged()}
-            <div style={{ width: `calc(100% - ${this.facetWidth()}px)`, marginLeft: `${this.facetWidth()}px` }}>
+            <div style={{ width: `calc(100% - ${this.facetWidth()}px)` }}>
                 {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             </div>
             {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
@@ -471,9 +488,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
                     :
                     ""))}
             {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
-                <div className="collectionTimeView-dragger" key="dragger" onPointerDown={this.onPointerDown} style={{ transform: `translate(${this.facetWidth()}px, 0px)` }} >
-                    <span title="library View Dragger" style={{ width: "5px", position: "absolute", top: "0" }} />
-                </div>
+                <div className="collectionTimeView-dragger" title="library View Dragger" onPointerDown={this.onPointerDown} style={{ right: this.facetWidth() - 10 }} />
             }
             {this.filterView}
         </div>);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 9529ef525..5203eb55f 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -61,7 +61,8 @@
                 pointer-events: all;
                 // margin-top: 10px;
             }
-            .collectionViewBaseChrome-template {
+            .collectionViewBaseChrome-template,
+            .collectionViewBaseChrome-viewModes {
                 display: grid;
                 background: rgb(238, 238, 238);
                 color:grey;
@@ -69,9 +70,12 @@
                 margin-bottom:auto;
                 margin-left: 5px;
             }
+            .collectionViewBaseChrome-viewModes {
+                margin-left: 25px;
+            }
 
             .collectionViewBaseChrome-viewSpecs {
-                margin-left: 25px;
+                margin-left: 5px;
                 display: grid;
                 
                 .collectionViewBaseChrome-filterIcon {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index cef85e148..7315d2c4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -276,12 +276,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             title="Collapse collection chrome" onClick={this.toggleCollapse}>
                             <FontAwesomeIcon icon="caret-up" size="2x" />
                         </button>
-                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
-                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
-                                <FontAwesomeIcon icon="filter" size="2x" />
-                            </div>
-                        </div>
-                        <div className="collectionViewBaseChrome-template" style={{ display: collapsed ? "none" : undefined }}>
+                        <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
                                 <div className="commandEntry-drop">
                                     <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
@@ -303,6 +298,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 </select>
                             </div>
                         </div>
+                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
+                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
+                                <FontAwesomeIcon icon="filter" size="2x" />
+                            </div>
+                        </div>
                         <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
                                 <div className="commandEntry-drop">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a77f09163..d98fe190c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -907,6 +907,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
     onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
         (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+        e.stopPropagation();
     }
     private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
         const { x, y, z } = viewDef;
@@ -991,6 +992,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                     {...this.getChildDocumentViewProps(pair.layout, pair.data)}
                     dataProvider={this.childDataProvider}
                     LayoutDoc={this.childLayoutDocFunc}
+                    pointerEvents={this.props.layoutEngine?.() !== undefined ? "none" : undefined}
                     jitterRotation={NumCast(this.props.Document.jitterRotation)}
                     fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
                     FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e7cb2c015..05ad98c43 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -22,6 +22,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     width?: number;
     height?: number;
     jitterRotation: number;
+    pointerEvents?: "none";
     transition?: string;
     fitToBox?: boolean;
 }
@@ -92,7 +93,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 height: this.height,
                 zIndex: this.ZInd,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground ? "none" : undefined
+                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents
             }} >
 
             {!this.props.fitToBox ?
-- 
cgit v1.2.3-70-g09d2


From a6d17ef79fb94334735998d3d11f7ea068dea93a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Apr 2020 03:15:25 -0400
Subject: cleaned up some more of the pivot view event handling api

---
 .../views/collections/CollectionTimeView.tsx       | 23 ++++++++++------------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +++++--
 3 files changed, 16 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 1760e2419..e05223ca0 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
@@ -26,18 +26,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     _changing = false;
     @observable _layoutEngine = "pivot";
     @observable _collapsed: boolean = false;
-    componentWillUnmount() {
-        this.props.Document.onChildClick = undefined;
-    }
+    @observable _childClickedScript: Opt<ScriptField>;
+    @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
-        const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
-        this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" });
-        this.props.Document._fitToBox = true;
-        if (!this.props.Document.onViewDefClick) {
-            this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
-        }
-        this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked;
-            DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const childText = "const alias = getAlias(this); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+        runInAction(() => {
+            this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
+            this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
+        });
     }
 
     layoutEngine = () => this._layoutEngine;
@@ -88,7 +85,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0a3577bc7..19e235ff2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -278,7 +278,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
-    get _facetWidth() { return NumCast(this.props.Document._facetWidth) }
+    get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
     set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
     bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d98fe190c..f19181a20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,6 +44,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CollectionViewType } from "../CollectionView";
+import { Script } from "vm";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -69,6 +70,8 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem
 const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
 export type collectionFreeformViewProps = {
     forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
+    childClickScript?: ScriptField;
+    viewDefDivClick?: ScriptField;
 };
 
 @observer
@@ -847,7 +850,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
     }
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
-    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
 
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
@@ -906,7 +909,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
     }
 
     onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
-        (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+        (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
         e.stopPropagation();
     }
     private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
-- 
cgit v1.2.3-70-g09d2


From ab007b636b99de38f7dee8ff08d1259dfc47e02a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 16 Apr 2020 21:47:56 -0400
Subject: fixed detail view for pivot views for templates

---
 src/client/views/collections/CollectionTimeView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index e05223ca0..1d1949c99 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -30,7 +30,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
         const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
-        const childText = "const alias = getAlias(this); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+        const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
             this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
-- 
cgit v1.2.3-70-g09d2


From 26949f548619c93fc13bc0f3cfcec6cf9e72f3d6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 18:50:21 -0400
Subject: final structural cleanp up of UserDocument for a while, hopefully

---
 src/client/documents/Documents.ts                  |   7 +-
 src/client/util/SelectionManager.ts                |   6 +-
 src/client/util/SharingManager.tsx                 |   2 +-
 src/client/views/DocumentButtonBar.tsx             |   5 +-
 src/client/views/DocumentDecorations.tsx           |   9 +-
 src/client/views/GlobalKeyHandler.ts               |   7 +-
 src/client/views/InkingControl.tsx                 |  18 +--
 src/client/views/MainView.tsx                      |  20 +--
 src/client/views/OverlayView.tsx                   |  23 ++-
 .../views/collections/CollectionDockingView.tsx    |  16 +-
 .../views/collections/CollectionTimeView.tsx       |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  15 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/PresBox.tsx                 |   8 +-
 src/mobile/ImageUpload.tsx                         |   2 +-
 src/mobile/MobileInterface.tsx                     |  53 +++----
 src/new_fields/Doc.ts                              |  18 ++-
 .../authentication/models/current_user_utils.ts    | 171 ++++++++++-----------
 18 files changed, 184 insertions(+), 202 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 209a72d52..d231f42e8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -8,7 +8,6 @@ import { PDFBox } from "../views/nodes/PDFBox";
 import { ScriptingBox } from "../views/nodes/ScriptingBox";
 import { VideoBox } from "../views/nodes/VideoBox";
 import { WebBox } from "../views/nodes/WebBox";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 import { OmitKeys, JSONUtils, Utils } from "../../Utils";
 import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc";
 import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
@@ -239,7 +238,7 @@ export namespace Docs {
             [DocumentType.LINKDB, {
                 data: new List<Doc>(),
                 layout: { view: EmptyBox, dataField: data },
-                options: { childDropAction: "alias", title: "LINK DB" }
+                options: { childDropAction: "alias", title: "Global Link Database" }
             }],
             [DocumentType.SCRIPTING, {
                 layout: { view: ScriptingBox, dataField: data }
@@ -960,7 +959,7 @@ export namespace DocUtils {
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", id?: string) {
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
-        if (target.doc === CurrentUserUtils.UserDocument) return undefined;
+        if (target.doc === Doc.UserDoc()) return undefined;
 
         const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id);
         Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1.title +" (" + (self.linkRelationship||"to") +") "  + self.anchor2.title');
@@ -990,7 +989,7 @@ export namespace DocUtils {
         });
         ContextMenu.Instance.addItem({
             description: "Add Template Doc ...",
-            subitems: DocListCast(Cast(Doc.UserDoc().expandingButtons, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({
+            subitems: DocListCast(Cast(Doc.UserDoc().dockedBtns, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({
                 description: ":" + StrCast(dragDoc.title),
                 event: (args: { x: number, y: number }) => {
                     const newDoc = Doc.ApplyTemplate(dragDoc);
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 6c386d684..a49977c42 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -28,21 +28,21 @@ export namespace SelectionManager {
                 manager.SelectedDocuments.clear();
                 manager.SelectedDocuments.set(docView, true);
             }
-            Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+            Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
         }
         @action
         DeselectDoc(docView: DocumentView): void {
             if (manager.SelectedDocuments.get(docView)) {
                 manager.SelectedDocuments.delete(docView);
                 docView.props.whenActiveChanged(false);
-                Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+                Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
             }
         }
         @action
         DeselectAll(): void {
             Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments.clear();
-            Doc.UserDoc().SelectedDocs = new List<Doc>([]);
+            Doc.UserDoc().activeSelection = new List<Doc>([]);
         }
     }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7496ac73c..3ce6de80d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -109,7 +109,7 @@ export default class SharingManager extends React.Component<{}> {
             if (isCandidate) {
                 const userDocument = await DocServer.GetRefField(user.userDocumentId);
                 if (userDocument instanceof Doc) {
-                    const notificationDoc = await Cast(userDocument.optionalRightCollection, Doc);
+                    const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc);
                     runInAction(() => {
                         if (notificationDoc instanceof Doc) {
                             this.users.push({ user, notificationDoc });
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 5b78008ab..93987b751 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -21,7 +21,6 @@ import { Template, Templates } from "./Templates";
 import React = require("react");
 import { DragManager } from '../util/DragManager';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -202,9 +201,9 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
     @computed
     get pinButton() {
         const targetDoc = this.view0?.props.Document;
-        const isPinned = targetDoc && CurrentUserUtils.IsDocPinned(targetDoc);
+        const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
         return !targetDoc ? (null) : <div className="documentButtonBar-linker"
-            title={CurrentUserUtils.IsDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
+            title={Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
             style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
 
             onClick={e => {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9ba418d74..c8766a6b3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,6 @@ import { Doc, DataSym } from "../../new_fields/Doc";
 import { PositionDocument } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
 import { DocUtils } from "../documents/Documents";
 import { DocumentType } from '../documents/DocumentTypes';
@@ -69,7 +68,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     get Bounds(): { x: number, y: number, b: number, r: number } {
         return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
             if (documentView.props.renderDepth === 0 ||
-                Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
+                Doc.AreProtosEqual(documentView.props.Document, Doc.UserDoc())) {
                 return bounds;
             }
             const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
@@ -164,7 +163,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         dragData.isSelectionMove = true;
         this.Interacting = true;
         this._hidden = true;
-        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(documentView => documentView.ContentDiv!), dragData, e.x, e.y, {
+        DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
             dragComplete: action(e => this._hidden = this.Interacting = false),
             hideSource: true
         });
@@ -178,14 +177,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onCloseClick = async (e: PointerEvent) => {
         if (e.button === 0) {
-            const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc;
+            const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
             const selected = SelectionManager.SelectedDocuments().slice();
             SelectionManager.DeselectAll();
             this._addedCloseCalls.forEach(handler => handler(selected));
 
             selected.map(dv => {
                 recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
-                dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
+                dv.props.removeDocument?.(dv.props.Document);
             });
         }
     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index d01f3f1e5..185222541 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -7,7 +7,6 @@ import { action, runInAction } from "mobx";
 import { Doc } from "../../new_fields/Doc";
 import { DictationManager } from "../util/DictationManager";
 import SharingManager from "../util/SharingManager";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
 import { ScriptField } from "../../new_fields/ScriptField";
 import { InkingControl } from "./InkingControl";
@@ -194,7 +193,7 @@ export default class KeyManager {
                 }
                 break;
             case "t":
-                PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 if (MainView.Instance.flyoutWidth === 240) {
                     MainView.Instance.flyoutWidth = 0;
                 } else {
@@ -202,7 +201,7 @@ export default class KeyManager {
                 }
                 break;
             case "l":
-                PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                PromiseValue(Cast(Doc.UserDoc()["tabs-button-library"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 if (MainView.Instance.flyoutWidth === 250) {
                     MainView.Instance.flyoutWidth = 0;
                 } else {
@@ -210,7 +209,7 @@ export default class KeyManager {
                 }
                 break;
             case "f":
-                PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                PromiseValue(Cast(Doc.UserDoc()["tabs-button-search"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 if (MainView.Instance.flyoutWidth === 400) {
                     MainView.Instance.flyoutWidth = 0;
                 } else {
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 645c7fa54..172c1864a 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -12,9 +12,9 @@ import { FormattedTextBox } from "./nodes/FormattedTextBox";
 
 export class InkingControl {
     @observable static Instance: InkingControl;
-    @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; }
-    @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; }
-    @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; }
+    @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; }
+    @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(Doc.UserDoc().inkColor)) ?? "rgb(244, 67, 54)"; }
+    @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "5"; }
     @observable public _open: boolean = false;
 
     constructor() {
@@ -23,7 +23,7 @@ export class InkingControl {
 
     switchTool = action((tool: InkTool): void => {
         // this._selectedTool = tool;
-        CurrentUserUtils.UserDocument.inkTool = tool;
+        Doc.UserDoc().inkTool = tool;
     });
     decimalToHexString(number: number) {
         if (number < 0) {
@@ -34,7 +34,7 @@ export class InkingControl {
 
     @undoBatch
     switchColor = action((color: ColorState): void => {
-        CurrentUserUtils.UserDocument.inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
+        Doc.UserDoc().inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
 
         if (InkingControl.Instance.selectedTool === InkTool.None) {
             const selected = SelectionManager.SelectedDocuments();
@@ -44,9 +44,9 @@ export class InkingControl {
                         view.props.Document.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document);
                 if (targetDoc) {
                     if (StrCast(Doc.Layout(view.props.Document).layout).indexOf("FormattedTextBox") !== -1 && FormattedTextBox.HadSelection) {
-                        Doc.Layout(view.props.Document).color = CurrentUserUtils.UserDocument.inkColor;
+                        Doc.Layout(view.props.Document).color = Doc.UserDoc().inkColor;
                     } else {
-                        Doc.Layout(view.props.Document)._backgroundColor = CurrentUserUtils.UserDocument.inkColor; // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
+                        Doc.Layout(view.props.Document)._backgroundColor = Doc.UserDoc().inkColor; // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
                     }
                 }
             });
@@ -57,7 +57,7 @@ export class InkingControl {
     @action
     switchWidth = (width: string): void => {
         // this._selectedWidth = width;
-        CurrentUserUtils.UserDocument.inkWidth = width;
+        Doc.UserDoc().inkWidth = width;
     }
 
     @computed
@@ -73,7 +73,7 @@ export class InkingControl {
     @action
     updateSelectedColor(value: string) {
         // this._selectedColor = value;
-        CurrentUserUtils.UserDocument.inkColor = value;
+        Doc.UserDoc().inkColor = value;
     }
 
     @computed
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0877cc5f6..f8e246315 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -204,7 +204,7 @@ export class MainView extends React.Component {
 
     @action
     createNewWorkspace = async (id?: string) => {
-        const workspaces = Cast(this.userDoc.workspaces, Doc) as Doc;
+        const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
         const workspaceCount = DocListCast(workspaces.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
@@ -214,8 +214,8 @@ export class MainView extends React.Component {
             title: "Collection " + workspaceCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
-        const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+        Doc.AddDocToList(Doc.GetProto(Doc.UserDoc().myDocuments as Doc), "data", freeformDoc);
+        const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myDocuments as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
 
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         mainDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!]);
@@ -261,7 +261,7 @@ export class MainView extends React.Component {
         }
         // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
         setTimeout(async () => {
-            const col = this.userDoc && await Cast(this.userDoc.optionalRightCollection, Doc);
+            const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc);
             col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col);
         }, 100);
         return true;
@@ -504,8 +504,8 @@ export class MainView extends React.Component {
         return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null);
     }
 
-    addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
-    remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
+    addButtonDoc = (doc: Doc) => Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
+    remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
     moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
 
     buttonBarXf = () => {
@@ -514,13 +514,13 @@ export class MainView extends React.Component {
         return new Transform(-translateX, -translateY, 1 / scale);
     }
     @computed get docButtons() {
-        const expandingBtns = Doc.UserDoc()?.expandingButtons;
-        if (expandingBtns instanceof Doc) {
+        const dockedBtns = Doc.UserDoc()?.dockedBtns;
+        if (dockedBtns instanceof Doc) {
             return <div className="mainView-docButtons" ref={this._docBtnRef}
-                style={{ height: !expandingBtns.linearViewIsExpanded ? "42px" : undefined }} >
+                style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
                 <MainViewNotifs />
                 <CollectionLinearView
-                    Document={expandingBtns}
+                    Document={dockedBtns}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     fieldKey={"data"}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 4000cade5..614217bc8 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,16 +1,14 @@
-import * as React from "react";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { observable, action, trace, computed } from "mobx";
-import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse, emptyPath } from "../../Utils";
-
-import './OverlayView.scss';
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
-import { DocListCast, Doc } from "../../new_fields/Doc";
+import * as React from "react";
+import { Doc, DocListCast } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
-import { DocumentView } from "./nodes/DocumentView";
-import { Transform } from "../util/Transform";
 import { NumCast } from "../../new_fields/Types";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils } from "../../Utils";
+import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
+import { DocumentView } from "./nodes/DocumentView";
+import './OverlayView.scss';
 
 export type OverlayDisposer = () => void;
 
@@ -140,10 +138,11 @@ export class OverlayView extends React.Component {
     }
 
     @computed get overlayDocs() {
-        if (!CurrentUserUtils.UserDocument) {
+        const userDocOverlays = Doc.UserDoc().myOverlayDocuments;
+        if (!userDocOverlays) {
             return (null);
         }
-        return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => {
+        return userDocOverlays instanceof Doc && DocListCast(userDocOverlays.data).map(d => {
             setTimeout(() => d.inOverlay = true, 0);
             let offsetx = 0, offsety = 0;
             const onPointerMove = action((e: PointerEvent) => {
@@ -195,7 +194,7 @@ export class OverlayView extends React.Component {
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}/>
+                    ContainingCollectionDoc={undefined} />
             </div>;
         });
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c74f5555b..5e77bc0bb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -14,11 +14,9 @@ import { List } from '../../../new_fields/List';
 import { FieldId } from "../../../new_fields/RefField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from '../../../new_fields/util';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Scripting } from '../../util/Scripting';
@@ -376,8 +374,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         // Because this is in a set timeout, if this component unmounts right after mounting,
                         // we will leak a GoldenLayout, because we try to destroy it before we ever create it
                         setTimeout(() => this.setupGoldenLayout(), 1);
-                        const userDoc = CurrentUserUtils.UserDocument;
-                        userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false);
+                        DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false);
                         this.props.Document.workspaceBrush = true;
                     }
                     this._ignoreStateChange = "";
@@ -544,9 +541,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     const theDoc = doc;
                     CollectionDockingView.Instance._removedDocs.push(theDoc);
 
-                    const userDoc = CurrentUserUtils.UserDocument;
-                    let recent: Doc | undefined;
-                    if (userDoc && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
+                    const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
+                    if (recent) {
                         Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                     }
                     SelectionManager.DeselectAll();
@@ -606,7 +602,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
                     if (doc instanceof Doc) {
                         let recent: Doc | undefined;
-                        if (CurrentUserUtils.UserDocument && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
+                        if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) {
                             Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                         }
                         const theDoc = doc;
@@ -681,7 +677,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @action
     public static PinDoc(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
         if (curPres) {
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
@@ -698,7 +694,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @action
     public static UnpinDoc(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
         if (curPres) {
             const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
             ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 1d1949c99..742a818bc 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -91,7 +91,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     public static SyncTimelineToPresentation(doc: Doc) {
         const fieldKey = Doc.LayoutFieldKey(doc);
-        doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(curPresentationItem()[this._pivotField || 'year'] || 0)");
+        doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(activePresentationItem()[this._pivotField || 'year'] || 0)");
     }
     specificMenu = (e: React.MouseEvent) => {
         const layoutItems: ContextMenuProps[] = [];
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c243d8f29..83e536167 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,10 +6,9 @@ import { observer } from "mobx-react";
 import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
-import { Document, listSpec, createSchema, makeInterface } from '../../../new_fields/Schema';
+import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -195,7 +194,7 @@ class TreeView extends React.Component<TreeViewProps> {
         })}
         onClick={() => {
             SelectionManager.DeselectAll();
-            Doc.UserDoc().SelectedDocs = new List([this.props.document]);
+            Doc.UserDoc().activeSelection = new List([this.props.document]);
             return false;
         }}
         OnTab={undoBatch((shift?: boolean) => {
@@ -691,14 +690,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
+        if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myWorkspaces) {
             ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.recentlyClosed) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => CurrentUserUtils.UserDocument.recentlyClosed = new List<Doc>(), icon: "plus" });
+        } else if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myRecentlyClosed) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
@@ -739,14 +738,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
                 heroView._showTitle = "title";
                 heroView._showTitleHover = "titlehover";
 
-                Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
+                Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
                     Docs.Create.FontIconDocument({
                         title: "hero view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
                         dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), icon: "portrait",
                         onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
                     }));
 
-                Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
+                Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
                     Docs.Create.FontIconDocument({
                         title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
                         dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), icon: "file-alt",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ca4ec7aff..196104fe0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -305,7 +305,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     self: this.rootDoc,
                     thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
                 }, console.log);
-                if (this.props.Document !== Doc.UserDoc().undoBtn && this.props.Document !== Doc.UserDoc().redoBtn) {
+                if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) {
                     UndoManager.RunInBatch(func, "on click");
                 } else func();
             } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
@@ -560,7 +560,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     static findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc()["icon-view-all"], Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data);
         const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data);
         const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index e428e16da..80d043db1 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -49,7 +49,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
     @computed get currentIndex() { return NumCast(this.layoutDoc._itemIndex); }
 
-    updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.rootDoc);
+    updateCurrentPresentation = action(() => Doc.UserDoc().activePresentation = this.rootDoc);
 
     next = () => {
         this.updateCurrentPresentation();
@@ -253,14 +253,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: CollectionViewType) => {
         if (BoolCast(this.layoutDoc.inOverlay) !== (mode === CollectionViewType.Invalid)) {
             if (this.layoutDoc.inOverlay) {
-                Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc);
+                Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
                 CollectionDockingView.AddRightSplit(this.rootDoc);
                 this.layoutDoc.inOverlay = false;
             } else {
                 this.layoutDoc.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
                 this.layoutDoc.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
                 this.props.addDocTab?.(this.rootDoc, "close");
-                Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc);
+                Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
             }
         }
     }));
@@ -292,7 +292,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.updateMinimize(e, StrCast(this.layoutDoc._viewType));
     });
 
-    childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
+    childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined;
     render() {
         const mode = StrCast(this.layoutDoc._viewType) as CollectionViewType;
         this.initializeViewAliases(this.childDocs, mode);
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 5903a2ce9..295e82142 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -67,7 +67,7 @@ class Uploader extends React.Component {
                         const field = await DocServer.GetRefField(res);
                         let pending: Opt<Doc>;
                         if (field instanceof Doc) {
-                            pending = await Cast(field.optionalRightCollection, Doc);
+                            pending = await Cast(field.rightSidebarCollection, Doc);
                         }
                         if (pending) {
                             this.status = "has pending docs";
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c87ac719c..73ebbb303 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -1,51 +1,38 @@
 import React = require('react');
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEraser, faHighlighter, faLongArrowAltLeft, faMousePointer, faPenNib } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { computed, action, observable } from 'mobx';
-import { CurrentUserUtils } from '../server/authentication/models/current_user_utils';
-import { FieldValue, Cast, StrCast } from '../new_fields/Types';
-import { Doc, DocListCast } from '../new_fields/Doc';
+import { DocServer } from '../client/DocServer';
 import { Docs } from '../client/documents/Documents';
-import { CollectionView } from '../client/views/collections/CollectionView';
-import { DocumentView } from '../client/views/nodes/DocumentView';
-import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, returnZero } from '../Utils';
-import { Transform } from '../client/util/Transform';
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice, faTrash, faCheck, faLongArrowAltLeft } from '@fortawesome/free-solid-svg-icons';
+import { DocumentManager } from '../client/util/DocumentManager';
+import RichTextMenu from '../client/util/RichTextMenu';
 import { Scripting } from '../client/util/Scripting';
-import { CollectionFreeFormView } from '../client/views/collections/collectionFreeForm/CollectionFreeFormView';
+import { Transform } from '../client/util/Transform';
+import { CollectionView } from '../client/views/collections/CollectionView';
+import { DocumentDecorations } from '../client/views/DocumentDecorations';
 import GestureOverlay from '../client/views/GestureOverlay';
 import { InkingControl } from '../client/views/InkingControl';
-import { InkTool } from '../new_fields/InkField';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import "./MobileInterface.scss";
-import { SelectionManager } from '../client/util/SelectionManager';
-import { DateField } from '../new_fields/DateField';
-import { GestureUtils } from '../pen-gestures/GestureUtils';
-import { DocServer } from '../client/DocServer';
-import { DocumentDecorations } from '../client/views/DocumentDecorations';
-import { OverlayView } from '../client/views/OverlayView';
-import { DictationOverlay } from '../client/views/DictationOverlay';
-import SharingManager from '../client/util/SharingManager';
-import { PreviewCursor } from '../client/views/PreviewCursor';
-import { ContextMenu } from '../client/views/ContextMenu';
+import { DocumentView } from '../client/views/nodes/DocumentView';
 import { RadialMenu } from '../client/views/nodes/RadialMenu';
-import PDFMenu from '../client/views/pdf/PDFMenu';
-import MarqueeOptionsMenu from '../client/views/collections/collectionFreeForm/MarqueeOptionsMenu';
-import GoogleAuthenticationManager from '../client/apis/GoogleAuthenticationManager';
-import { listSpec } from '../new_fields/Schema';
+import { PreviewCursor } from '../client/views/PreviewCursor';
+import { Doc, DocListCast, FieldResult } from '../new_fields/Doc';
 import { Id } from '../new_fields/FieldSymbols';
-import { DocumentManager } from '../client/util/DocumentManager';
-import RichTextMenu from '../client/util/RichTextMenu';
+import { InkTool } from '../new_fields/InkField';
+import { listSpec } from '../new_fields/Schema';
+import { Cast, FieldValue } from '../new_fields/Types';
 import { WebField } from "../new_fields/URLField";
-import { FieldResult } from "../new_fields/Doc";
-import { List } from '../new_fields/List';
+import { CurrentUserUtils } from '../server/authentication/models/current_user_utils';
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
+import "./MobileInterface.scss";
 
 library.add(faLongArrowAltLeft);
 
 @observer
 export default class MobileInterface extends React.Component {
     @observable static Instance: MobileInterface;
-    @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
+    @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
     // @observable private currentView: "main" | "ink" | "upload" = "main";
     private mainDoc: any = CurrentUserUtils.setupMobileDoc(this.userDoc);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index bcf0d1aec..8e8f0928c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -865,6 +865,16 @@ export namespace Doc {
         return id;
     }
 
+    export function isDocPinned(doc: Doc) {
+        //add this new doc to props.Document
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        if (curPres) {
+            return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
+        }
+        return false;
+    }
+
+
     export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
         let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
         if (!(optionsCollection instanceof Doc)) {
@@ -906,13 +916,13 @@ Scripting.addGlobal(function redo() { return UndoManager.Redo(); });
 Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
 Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
 Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
-Scripting.addGlobal(function curPresentationItem() {
-    const curPres = Doc.UserDoc().curPresentation as Doc;
+Scripting.addGlobal(function activePresentationItem() {
+    const curPres = Doc.UserDoc().activePresentation as Doc;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
-Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().SelectedDocs = new List([doc]); });
+Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); });
 Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
-    const docs = DocListCast(Doc.UserDoc().SelectedDocs).
+    const docs = DocListCast(Doc.UserDoc().activeSelection).
         filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP &&
             (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
     return docs.length ? new List(docs) : prevValue;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 3d68d8425..a2f016f94 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -30,7 +30,7 @@ export class CurrentUserUtils {
     public static get MainDocId() { return this.mainDocId; }
     public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
     @computed public static get UserDocument() { return Doc.UserDoc(); }
-    @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).pen as Doc; }
+    @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).inkPen as Doc; }
 
     @observable public static GuestTarget: Doc | undefined;
     @observable public static GuestWorkspace: Doc | undefined;
@@ -84,10 +84,12 @@ export class CurrentUserUtils {
 
         if (doc["template-buttons"] === undefined) {
             doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], {
-                title: "template buttons", _xMargin: 0, _showTitle: "title",
+                title: "Template Item Creators", _xMargin: 0, _showTitle: "title",
                 _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
+        } else {
+            DocListCast(Cast(doc["template-buttons"], Doc, null)?.data); // prefetch templates
         }
         return doc["template-buttons"] as Doc;
     }
@@ -114,9 +116,10 @@ export class CurrentUserUtils {
             Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
             doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt),
                 { title: "Note Layouts", _height: 75 }));
+        } else {
+            DocListCast(Cast(doc.noteTypes, Doc, null)?.data); // prefetch templates
         }
-        // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
-        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+
         return doc.noteTypes as Doc;
     }
 
@@ -135,29 +138,31 @@ export class CurrentUserUtils {
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc) {
-        if (doc["icon-view"] === undefined) {
+        if (doc["template-icon-view"] === undefined) {
             const iconView = Docs.Create.TextDocument("", {
                 title: "icon", _width: 150, _height: 30, isTemplateDoc: true,
                 onClick: ScriptField.MakeScript("deiconifyView(this)")
             });
             Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
             iconView.isTemplateDoc = makeTemplate(iconView);
-            doc["icon-view"] = new PrefetchProxy(iconView);
+            doc["template-icon-view"] = new PrefetchProxy(iconView);
         }
-        if (doc["icon-view-img"] === undefined) {
+        if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
-            doc["icon-view-img"] = new PrefetchProxy(iconImageView);
+            doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
-        if (doc["icon-view-col"] === undefined) {
+        if (doc["template-icon-view-col"] === undefined) {
             const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
-            doc["icon-view-col"] = new PrefetchProxy(iconColView);
+            doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
-        if (doc["icon-view-all"] === undefined) {
-            doc["icon-view-all"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["icon-view"] as Doc, doc["icon-view-img"] as Doc, doc["icon-view-col"] as Doc], { title: "icon templates", _height: 75 }));
+        if (doc["template-icons"] === undefined) {
+            doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-col"] as Doc], { title: "icon templates", _height: 75 }));
+        } else {
+            DocListCast(Cast(doc["template-icons"], Doc, null)?.data); // prefetch templates
         }
-        return doc["icon-view-all"] as Doc;
+        return doc["template-icons"] as Doc;
     }
 
     static creatorBtnDescriptors(doc: Doc): {
@@ -180,15 +185,15 @@ export class CurrentUserUtils {
             { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
             { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
             { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
-            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
+            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
             { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
             { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
-            // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen,  this)`, activePen: doc },
-            // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
-            // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
+            // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen,  this)`, activePen: doc },
+            // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
+            // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
             { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
             { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
             // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
@@ -198,7 +203,7 @@ export class CurrentUserUtils {
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
     static async setupCreatorButtons(doc: Doc) {
         let alreadyCreatedButtons: string[] = [];
-        const dragCreatorSet = await Cast(doc.dragCreators, Doc, null);
+        const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null);
         if (dragCreatorSet) {
             const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
             if (dragCreators) {
@@ -223,25 +228,25 @@ export class CurrentUserUtils {
             }));
 
         if (dragCreatorSet === undefined) {
-            doc.dragCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
-                title: "Draggable Items", _showTitle: "title", _xMargin: 0,
+            doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
+                title: "Standard Item Creators", _showTitle: "title", _xMargin: 0,
                 _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
         } else {
-            creatorBtns.forEach(nb => Doc.AddDocToList(doc.dragCreators as Doc, "data", nb));
+            creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb));
         }
-        return doc.dragCreators as Doc;
+        return doc.myItemCreators as Doc;
     }
 
     static setupMobileButtons(doc: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
             { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" },
-            { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen,  this)`, activePen: doc },
-            { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
-            { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
-            // { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc },
+            { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen,  this)`, activePen: doc },
+            { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
+            { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
+            // { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "red", activePen: doc },
             { title: "upload", icon: "upload", click: 'switchMobileView(setupMobileUploadDoc, renderMobileUpload, onSwitchMobileUpload);', backgroundColor: "orange" },
             // { title: "upload", icon: "upload", click: 'uploadImageMobile();', backgroundColor: "cyan" },
         ];
@@ -255,11 +260,11 @@ export class CurrentUserUtils {
 
     static setupThumbButtons(doc: Doc) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen,  this)`, activePen: doc },
-            { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
-            { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+            { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen,  this)`, activePen: doc },
+            { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+            { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
         ];
         return docProtoData.map(data => Docs.Create.FontIconDocument({
             _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
@@ -316,25 +321,25 @@ export class CurrentUserUtils {
         const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
         const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
 
-        if (doc.allCreators === undefined) {
-            doc.allCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
+        if (doc.myCreators === undefined) {
+            doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
                 title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
                 _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
             }));
         }
         // setup a color picker
-        if (doc.colorPicker === undefined) {
+        if (doc.myColorPicker === undefined) {
             const color = Docs.Create.ColorDocument({
                 title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
             });
-            doc.colorPicker = new PrefetchProxy(color);
+            doc.myColorPicker = new PrefetchProxy(color);
         }
 
         if (doc["tabs-button-tools"] === undefined) {
             doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({
                 _width: 35, _height: 25, title: "Tools", fontSize: 10,
                 letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-                sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.allCreators as Doc, doc.colorPicker as Doc], {
+                sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
                     _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
                 })) as any as Doc,
                 targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
@@ -347,39 +352,39 @@ export class CurrentUserUtils {
 
     static setupWorkspaces(doc: Doc) {
         // setup workspaces library item
-        if (doc.worspaces === undefined) {
-            doc.workspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        if (doc.myWorkspaces === undefined) {
+            doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
             }));
         }
         const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
-        (doc.workspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
-        (doc.workspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
+        (doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
+        (doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
 
-        return doc.workspaces as Doc;
+        return doc.myWorkspaces as Doc;
     }
     static setupDocumentCollection(doc: Doc) {
-        if (doc.documents === undefined) {
-            doc.documents = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        if (doc.myDocuments === undefined) {
+            doc.myDocuments = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
             }));
         }
-        return doc.documents as Doc;
+        return doc.myDocuments as Doc;
     }
     static setupRecentlyClosed(doc: Doc) {
         // setup Recently Closed library item
-        if (doc.recentlyClosed === undefined) {
-            doc.recentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        if (doc.myRecentlyClosed === undefined) {
+            doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
             }));
         }
         // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
-        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
+        PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
         const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
-        (doc.recentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-        (doc.recentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+        (doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+        (doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
 
-        return doc.recentlyClosed as Doc;
+        return doc.myRecentlyClosed as Doc;
     }
     // setup the Library button which will display the library panel.  This panel includes a collection of workspaces, documents, and recently closed views
     static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) {
@@ -456,48 +461,48 @@ export class CurrentUserUtils {
     })) as any as Doc
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
-    static setupExpandingButtons(doc: Doc) {
-        if (doc.penBtn === undefined) {
-            doc.penBtn = CurrentUserUtils.ficon({
-                onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
-                author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
+    static setupDockedButtons(doc: Doc) {
+        if (doc["dockedBtn-pen"] === undefined) {
+            doc["dockedBtn-pen"] = CurrentUserUtils.ficon({
+                onClick: ScriptField.MakeScript("activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)"),
+                author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.inkPen,  this)`), activePen: doc
             });
         }
-        if (doc.undoBtn === undefined) {
-            doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
+        if (doc["dockedBtn-undo"] === undefined) {
+            doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
         }
-        if (doc.redoBtn === undefined) {
-            doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
+        if (doc["dockedBtn-redo"] === undefined) {
+            doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
         }
-        if (doc.expandingButtons === undefined) {
-            doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
+        if (doc.dockedBtns === undefined) {
+            doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc, doc["dockedBtn-pen"] as Doc]);
         }
     }
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
-        if (doc.overlays === undefined) {
-            doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
+        if (doc.myOverlayDocuments === undefined) {
+            doc.myOverlayDocuments = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6" }));
         }
     }
 
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
-        if (doc.presentationTemplate === undefined) {
-            doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
+        if (doc["template-presentation"] === undefined) {
+            doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
             }));
         }
-        if (doc.curPresentation === undefined) {
-            doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), {
+        if (doc.activePresentation === undefined) {
+            doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
                 title: "Presentation", _viewType: CollectionViewType.Stacking,
                 _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
             });
         }
     }
 
-    static setupMobileUploads(doc: Doc) {
-        if (doc.optionalRightCollection === undefined) {
-            doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" }));
+    static setupRightSidebar(doc: Doc) {
+        if (doc.rightSidebarCollection === undefined) {
+            doc.rightSidebarCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Right Sidebar" }));
         }
     }
 
@@ -532,29 +537,19 @@ export class CurrentUserUtils {
         doc.title = Doc.CurrentUserEmail;
         doc.activePen = doc;
         CurrentUserUtils.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
-        CurrentUserUtils.setupMobileUploads(doc);  // sets up the optional right collection of documents uploaded from mobile
+        CurrentUserUtils.setupRightSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
         CurrentUserUtils.setupOverlays(doc);  // documents in overlay layer 
-        CurrentUserUtils.setupExpandingButtons(doc);  // the bottom bar of font icons
+        CurrentUserUtils.setupDockedButtons(doc);  // the bottom bar of font icons
         CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered
         await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
-        doc.linkDb = Docs.Prototypes.MainLinkDocument();
+        doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument();
 
         // 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
-        doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
-        doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
+        doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+        doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
         return doc;
     }
-
-    public static IsDocPinned(doc: Doc) {
-        //add this new doc to props.Document
-        const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
-        if (curPres) {
-            return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
-        }
-        return false;
-    }
-
     public static async loadCurrentUser() {
         return rp.get(Utils.prepend("/getCurrentUser")).then(response => {
             if (response) {
-- 
cgit v1.2.3-70-g09d2


From 72f614f023a4bac6a2f0b63a1b1a16e6f4545c7a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 23 Apr 2020 00:53:08 -0400
Subject: fixed browser back after link navigation. simplified menus more.
 fixed jitter mode to be seeded. fixed menu gear for  collections. .

---
 src/client/util/History.ts                         |  6 +++-
 src/client/util/ProsemirrorExampleTransfer.ts      |  4 +--
 src/client/views/DocumentDecorations.tsx           |  6 +++-
 .../views/collections/CollectionDockingView.tsx    |  6 +++-
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  4 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 21 +++++------
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  9 +++--
 src/client/views/nodes/DocumentBox.tsx             |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 41 ++++++++++++++++++++--
 src/client/views/nodes/KeyValueBox.tsx             | 23 ++++++++----
 src/client/views/nodes/LinkAnchorBox.tsx           |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  2 +-
 src/client/views/nodes/SliderBox.tsx               |  2 +-
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 20 files changed, 103 insertions(+), 39 deletions(-)

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

diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 545e8acb4..2c53d7e52 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -40,8 +40,12 @@ export namespace HistoryUtil {
         // }
     }
 
+    let _lastStatePush = 0;
     export function pushState(state: ParsedUrl) {
-        history.pushState(state, "", createUrl(state));
+        if (Date.now() - _lastStatePush > 1000) {
+            history.pushState(state, "", createUrl(state));
+        }
+        _lastStatePush = Date.now();
     }
 
     export function replaceState(state: ParsedUrl) {
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index 42247f177..680f48f70 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -154,7 +154,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         const originalDoc = layoutDoc.rootDocument || layoutDoc;
         if (originalDoc instanceof Doc) {
             const newDoc = Docs.Create.TextDocument("", {
-                title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
+                layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
                 x: NumCast(originalDoc.x), y: NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10, _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
             });
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -172,7 +172,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         const originalDoc = layoutDoc.rootDocument || layoutDoc;
         if (force || props.Document._singleLine) {
             const newDoc = Docs.Create.TextDocument("", {
-                title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
+                layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
                 x: NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10, y: NumCast(originalDoc.y), _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
             });
             FormattedTextBox.SelectOnLoad = newDoc[Id];
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 43c7751fa..6e699bc68 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -134,7 +134,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action onSettingsClick = (e: PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey) {
             let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
-            while (child.children.length) child = child.children[0];
+            while (child.children.length) {
+                const next = Array.from(child.children).find(c => !c.className.includes("collectionViewChrome"));
+                if (next) child = next;
+                else break;
+            }
             simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         }
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5e77bc0bb..0d859c3f1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -29,6 +29,7 @@ import "./CollectionDockingView.scss";
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { DockingViewButtonSelector } from './ParentDocumentSelector';
 import React = require("react");
+import { CollectionViewType } from './CollectionView';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -93,6 +94,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @undoBatch
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
+        if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
+            return MainView.Instance.openWorkspace(docView.props.Document);
+        }
         const document = Doc.MakeAlias(docView.props.Document);
         const newItemStackConfig = {
             type: 'stack',
@@ -771,7 +775,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc.dockingConfig) {
+        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b15649d83..24a3119cc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -397,7 +397,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
-            ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
         }
     }
 
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 742a818bc..06ebf6d2d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -102,7 +102,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         layoutItems.push({ description: "Auto Time/Pivot layout", event: () => { doc._forceRenderEngine = undefined; }, icon: "compress-arrows-alt" });
         layoutItems.push({ description: "Sync with presentation", event: () => CollectionTimeView.SyncTimelineToPresentation(doc), icon: "compress-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Pivot/Time Options ...", subitems: layoutItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
     }
     @computed get _allFacets() {
         const facets = new Set<string>();
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f589c2c76..a052d045c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -711,7 +711,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
             layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
-            ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
         ContextMenu.Instance.addItem({
             description: "Buxton Layout", icon: "eye", event: () => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ade54f2c9..4f1242c33 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -229,7 +229,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
                 return newRendition;
             }, false);
 
-            const existing = ContextMenu.Instance.findByDescription("Layout...");
+            const existing = ContextMenu.Instance.findByDescription("Options...");
             const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             if (this.props.Document.childLayout instanceof Doc) {
@@ -240,7 +240,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
-            !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
+            !existing && ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
 
             const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
             const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3b5101a4d..55e55e56c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1048,16 +1048,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     onContextMenu = (e: React.MouseEvent) => {
         if (this.props.children && this.props.annotationsKey) return;
-        const layoutItems: ContextMenuProps[] = [];
-
-        layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
-        layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
-        layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
-        layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
-        layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
+        const options = ContextMenu.Instance.findByDescription("Options...");
+        const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+
+        optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+        optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+        optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
+        optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
         // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
-        layoutItems.push({
+        optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
+        optionItems.push({
             description: "Import document", icon: "upload", event: ({ x, y }) => {
                 const input = document.createElement("input");
                 input.type = "file";
@@ -1085,7 +1086,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
         });
 
-        ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
     }
 
     private childViews = () => {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8f40ea2be..6ff6d1b42 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -162,7 +162,7 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     stopRecording = action(() => {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3a7e005ac..615b05e43 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,3 @@
-import anime from "animejs";
 import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -29,8 +28,14 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
     @observable _animPos: number[] | undefined = undefined;
+    random(min: number, max: number) { // min should not be equal to max
+        const mseed = Math.abs(this.X * this.Y);
+        const seed = (mseed * 9301 + 49297) % 233280;
+        var rnd = seed / 233280;
+        return min + rnd * (max - min);
+    }
     get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
-    get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${anime.random(-1, 1) * this.props.jitterRotation}deg)`; }
+    get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
     get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
     get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
     get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index ac562f19a..7583aa070 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -45,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         funcs.push({ description: (this.props.Document.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => Doc.GetProto(this.props.Document).excludeCollections = !this.props.Document.excludeCollections, icon: "expand-arrows-alt" });
         funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
 
-        ContextMenu.Instance.addItem({ description: "DocumentBox Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
     @computed get contentDoc() {
         return (this.props.Document.isTemplateDoc || this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a19ba6506..cab7b965a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -741,7 +741,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const existingOnClick = cm.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-        onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
+        onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
         onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
         onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
         onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2c89d53d8..1329f7dcf 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
 import { ReplaceStep } from 'prosemirror-transform';
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../new_fields/DateField';
-import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
@@ -48,6 +48,7 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './Format
 import React = require("react");
 import { PrefetchProxy } from '../../../new_fields/Proxy';
 import { makeTemplate } from '../../util/DropConverter';
+import { DocumentView } from './DocumentView';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -396,6 +397,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null);
     }
     specificContextMenu = (e: React.MouseEvent): void => {
+        const cm = ContextMenu.Instance;
+
         const funcs: ContextMenuProps[] = [];
         this.props.Document.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" });
         funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
@@ -407,10 +410,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         });
         funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Toggle Audio", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Dictation Icon", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
+
+        const highlighting: ContextMenuProps[] = [];
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
-            funcs.push({
+            highlighting.push({
                 description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
                     if (FormattedTextBox._highlights.indexOf(option) === -1) {
@@ -421,8 +426,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     this.updateHighlights();
                 }, icon: "expand-arrows-alt"
             }));
+        funcs.push({ description: "highlighting...", subitems: highlighting, icon: "hand-point-right" });
 
         ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+
+        const change = cm.findByDescription("Change Perspective...");
+        const changeItems: ContextMenuProps[] = change && "subitems" in change ? change.subitems : [];
+
+        const noteTypesDoc = Cast(Doc.UserDoc().noteTypes, Doc, null);
+        const noteTypes = DocListCast(noteTypesDoc?.data);
+        noteTypes.forEach(note => {
+            changeItems.push({
+                description: StrCast(note.title), event: () => {
+                    Doc.setNativeView(this.props.Document);
+                    DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
+                }, icon: "eye"
+            })
+        });
+        changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" })
+        !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
+
+        const open = cm.findByDescription("Open New Perspective...");
+        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+
+        openItems.push({
+            description: "FreeForm", event: () => {
+                const alias = Doc.MakeAlias(this.rootDoc);
+                DocumentView.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform");
+                this.props.addDocTab(alias, "onRight");
+            }, icon: "eye"
+        })
+        !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" });
+
     }
 
     recordDictation = () => {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 7aad6f90e..2970674a2 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -16,6 +16,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./KeyValueBox.scss";
 import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 
 export type KVPScript = {
     script: CompiledScript;
@@ -34,11 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-    get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; }
-
-    constructor(props: FieldViewProps) {
-        super(props);
-    }
+    get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
 
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
@@ -234,13 +232,26 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         return new Doc;
     }
 
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const cm = ContextMenu.Instance;
+        const open = cm.findByDescription("Change Perspective...");
+        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+        openItems.push({
+            description: "Default Perspective", event: () => {
+                this.props.addDocTab(this.fieldDocToLayout, "inTab");
+                this.props.addDocTab(this.props.Document, "close");
+            }, icon: "image"
+        });
+        !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
+    }
+
     render() {
         const dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
                 <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
             </div>;
 
-        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} ref={this._mainCont}>
+        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}>
             <table className="keyValueBox-table">
                 <tbody className="keyValueBox-tbody">
                     <tr className="keyValueBox-header">
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 8e3c15908..eb647d0e4 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -109,7 +109,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
         funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" });
         funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" });
 
-        ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6db36e43c..3712c648e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -210,7 +210,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     @computed get contentScaling() { return this.props.ContentScaling(); }
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 11b24b059..125690dc7 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -120,7 +120,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
                 }), icon: "expand-arrows-alt"
             });
-            ContextMenu.Instance.addItem({ description: "Screenshot Funcs...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
 
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 746ea0b64..b2d451ea8 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -40,7 +40,7 @@ export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocume
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) });
-        ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
     onChange = (values: readonly number[]) => runInAction(() => {
         this.dataDoc[this.minThumbKey] = values[0];
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 588068334..613929bca 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -209,7 +209,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
                 }), icon: "expand-arrows-alt"
             });
-            ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From edf82f7a686169ea279627bac1c661aaa3459ec9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 24 Apr 2020 11:44:03 -0400
Subject: more starburst tweaks.

---
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  5 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  6 +--
 src/client/views/nodes/DocumentBox.tsx             |  2 +-
 src/new_fields/Doc.ts                              | 43 +++++++++++++---------
 5 files changed, 33 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 06ebf6d2d..63498cea1 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -29,7 +29,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
-        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
         const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 57f62347a..7152ff242 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -108,7 +108,8 @@ export function computerStarburstLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
     const docMap = new Map<Doc, ViewDefBounds>();
-    const burstDim = [NumCast(pivotDoc.starburstRadius, panelDim[0]), NumCast(pivotDoc.starburstRadius, panelDim[1])]
+    const burstDim = [NumCast(pivotDoc.starburstRadius, panelDim[0]), NumCast(pivotDoc.starburstRadius, panelDim[1])];
+    const scaleDim = [NumCast(pivotDoc._starburstWidth, burstDim[0]) * 5, NumCast(pivotDoc._starburstHeight, burstDim[1]) * 5];
     childDocs.forEach((doc, i) => {
         const deg = i / childDocs.length * Math.PI * 2;
         docMap.set(doc, {
@@ -120,7 +121,7 @@ export function computerStarburstLayout(
             payload: undefined
         });
     });
-    return normalizeResults(burstDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c)));
+    return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c)));
 }
 
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 63199ccbb..1f0bc89c5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -898,7 +898,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             return true;
         }
         return this.props.addDocTab(doc, where);
-    })
+    });
     getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
         const result = this.Document.arrangeScript?.script.run(params, console.log);
         if (result?.success) {
@@ -980,7 +980,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const newPool = new Map<string, any>();
         const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.();
         switch (engine) {
-            case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) }
+            case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
             case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
             case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
             case "starburst": return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
@@ -1044,7 +1044,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             doc.x = scr?.[0];
             doc.y = scr?.[1];
             this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc);
-        })
+        });
         this.props.ContainingCollectionView?.removeDocument(this.props.Document);
     }));
     layoutDocsInGrid = () => {
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 7583aa070..0111cadd5 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -111,7 +111,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         const childTemplateName = StrCast(this.props.Document.childTemplateName);
         if (containedDoc && childTemplateName && !containedDoc["layout_" + childTemplateName]) {
             setTimeout(() => {
-                DocumentView.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName);
+                Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName);
                 Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined);
             }, 0);
         }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 52c3ce141..48606833c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -998,23 +998,7 @@ export namespace Doc {
 
         newCollection._layoutEngine = "pass";
         newCollection.overflow = "visible";
-        const script = "if (self._layoutEngine === 'starburst') {" +
-            "     self.overflow = undefined;" +
-            "     self.x = self.x + self._width/2 - 55; " +
-            "     self.y = self.y + self._height/2 - 55; " +
-            "     self._width = self._height = 110;" +
-            "     self._layoutEngine = 'pass';" +
-            " } else {" +
-            "    self.overflow = 'visible';" +
-            "    !self.starburstRadius && (self.starburstRadius = 500);" +
-            "    if (self._layoutEngine === 'pass') { " +
-            "       self.x = self.x + self._width/2 - 12.5; " +
-            "       self.y = self.y + self._height/2 - 12.5; " +
-            "    }; " +
-            "    self._width = self._height = 25;" +
-            "    self._layoutEngine = 'starburst';" +
-            " };";
-        newCollection.onClick = ScriptField.MakeScript(script, { self: Doc.name });
+        newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self)", { self: Doc.name });
     }
 
 
@@ -1071,4 +1055,27 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
     return docs.length ? new List(docs) : prevValue;
 });
 Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
+Scripting.addGlobal(function toggleStarburst(self: Doc) {
+    if (self._layoutEngine === 'starburst') {
+        const defaultSize = 110;
+        self.overflow = undefined;
+        self.x = NumCast(self.x) + self[WidthSym]() / 2 - NumCast(self._starburstWidth, defaultSize) / 2;
+        self.y = NumCast(self.y) + self[HeightSym]() / 2 - NumCast(self._starburstHeight, defaultSize) / 2;
+        self._width = NumCast(self._starburstWidth, defaultSize);
+        self._height = NumCast(self._starburstHeight, defaultSize);
+        self._layoutEngine = 'pass';
+    } else {
+        const defaultSize = 25;
+        self.overflow = 'visible';
+        !self.starburstRadius && (self.starburstRadius = 200);
+        if (self._layoutEngine === 'pass') {
+            self.x = NumCast(self.x) + self[WidthSym]() / 2 - defaultSize / 2;
+            self.y = NumCast(self.y) + self[HeightSym]() / 2 - defaultSize / 2;
+            self._starburstWidth = self._width;
+            self._starburstHeight = self._height;
+        }
+        self._width = self._height = defaultSize;
+        self._layoutEngine = 'starburst';
+    }
+});
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From efd038bd12de35abc80141b130be523e3fea077a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 25 Apr 2020 03:42:51 -0400
Subject: hopefully final cleanup of pile up collection view.   added doubleTap
 support in setupMoveUpEvents

---
 src/Utils.ts                                       | 12 ++-
 src/client/documents/Documents.ts                  |  4 +
 .../views/collections/CollectionPileView.scss      |  8 ++
 .../views/collections/CollectionPileView.tsx       | 97 ++++++++++++++++++++++
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../views/collections/CollectionTimeView.tsx       |  3 +-
 src/client/views/collections/CollectionView.tsx    |  5 +-
 .../CollectionFreeFormLayoutEngines.tsx            | 95 ++++++++++-----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 +++--
 .../collections/collectionFreeForm/MarqueeView.tsx | 16 ++--
 src/new_fields/Doc.ts                              | 35 ++------
 11 files changed, 189 insertions(+), 109 deletions(-)
 create mode 100644 src/client/views/collections/CollectionPileView.scss
 create mode 100644 src/client/views/collections/CollectionPileView.tsx

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 9acdc8731..ad12c68a1 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -504,8 +504,9 @@ export function setupMoveUpEvents(
     e: React.PointerEvent,
     moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
     upEvent: (e: PointerEvent) => void,
-    clickEvent: (e: PointerEvent) => void,
-    stopPropagation: boolean = true
+    clickEvent: (e: PointerEvent, doubleTap?: boolean) => void,
+    stopPropagation: boolean = true,
+    stopMovePropagation: boolean = true
 ) {
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
@@ -520,12 +521,15 @@ export function setupMoveUpEvents(
         }
         (target as any)._lastX = e.clientX;
         (target as any)._lastY = e.clientY;
-        e.stopPropagation();
+        stopMovePropagation && e.stopPropagation();
     };
+    (target as any)._doubleTap = false;
     const _upEvent = (e: PointerEvent): void => {
+        (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < 300);
+        (target as any)._lastTap = Date.now();
         upEvent(e);
         if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) {
-            clickEvent(e);
+            clickEvent(e, (target as any)._doubleTap);
         }
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 578d337d6..1268c97b0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -612,6 +612,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id);
         }
 
+        export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Pile }, id);
+        }
+
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id);
         }
diff --git a/src/client/views/collections/CollectionPileView.scss b/src/client/views/collections/CollectionPileView.scss
new file mode 100644
index 000000000..ac874b663
--- /dev/null
+++ b/src/client/views/collections/CollectionPileView.scss
@@ -0,0 +1,8 @@
+.collectionPileView {
+    display: flex;
+    flex-direction: row;
+    position: absolute;
+    height: 100%;
+    width: 100%;
+    overflow: visible;
+}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
new file mode 100644
index 000000000..511efe0a7
--- /dev/null
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -0,0 +1,97 @@
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { CollectionSubView } from "./CollectionSubView";
+import "./CollectionPileView.scss";
+import React = require("react");
+import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+
+@observer
+export class CollectionPileView extends CollectionSubView(doc => doc) {
+    _lastTap = 0;
+    _doubleTap: boolean | undefined = false;
+    _originalChrome: string = "";
+    @observable _contentsActive = true;
+    @observable _layoutEngine = "pass";
+    @observable _collapsed: boolean = false;
+    @observable _childClickedScript: Opt<ScriptField>;
+    componentDidMount() {
+        this._originalChrome = StrCast(this.layoutDoc._chromeStatus);
+        this.layoutDoc._chromeStatus = "disabled";
+        this.layoutDoc.hideFilterView = true;
+    }
+    componentWillUnmount() {
+        this.layoutDoc.hideFilterView = false;
+        this.layoutDoc._chromeStatus = this._originalChrome;
+    }
+
+    layoutEngine = () => this._layoutEngine;
+
+    @computed get contents() {
+        return <div className="collectionPileView-innards" style={{ width: "100%", pointerEvents: this._contentsActive && (this.props.active() || this.layoutEngine() === "starburst") ? undefined : "none" }} >
+            <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} />
+        </div>;
+    }
+
+    specificMenu = (e: React.MouseEvent) => {
+        const layoutItems: ContextMenuProps[] = [];
+        const doc = this.props.Document;
+
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
+    }
+
+    toggleStarburst = action(() => {
+        if (this._layoutEngine === 'starburst') {
+            const defaultSize = 110;
+            this.layoutDoc.overflow = undefined;
+            this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
+            this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
+            this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
+            this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
+            this._layoutEngine = 'pass';
+        } else {
+            const defaultSize = 25;
+            this.layoutDoc.overflow = 'visible';
+            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
+            !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
+            if (this._layoutEngine === 'pass') {
+                this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
+                this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
+                this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
+                this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym]();
+            }
+            this.layoutDoc._width = this.layoutDoc._height = defaultSize;
+            this._layoutEngine = 'starburst';
+        }
+    });
+
+    pointerDown = (e: React.PointerEvent) => {
+        // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false, false); // this sets _doubleTap
+    }
+
+    onClick = (e: React.MouseEvent) => {
+        if (e.button === 0 && (this._doubleTap || this.layoutEngine() === "starburst")) {
+            SelectionManager.DeselectAll();
+            this.toggleStarburst();
+            e.stopPropagation();
+        } else if (this.layoutEngine() === "pass") {
+            runInAction(() => this._contentsActive = false);
+            setTimeout(action(() => this._contentsActive = true), 300);
+        }
+    }
+
+    render() {
+
+        return <div className={"collectionPileView"} onContextMenu={this.specificMenu} onClick={this.onClick} onPointerDown={this.pointerDown}
+            style={{ width: this.props.PanelWidth(), height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
+            {this.contents}
+        </div>;
+    }
+}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 78ba3d6e6..c530b24fb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -400,9 +400,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             if (generatedDocuments.length) {
                 const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d));
                 if (set) {
-                    const pile = Docs.Create.FreeformDocument(generatedDocuments, { ...options, title: "pile", _LODdisable: true, });
-                    Doc.pileup(pile, generatedDocuments);
-                    addDocument(pile);
+                    addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!));
                 } else {
                     generatedDocuments.forEach(addDocument);
                 }
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 63498cea1..045134225 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -19,7 +19,6 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 import React = require("react");
-import { DocumentView } from "../nodes/DocumentView";
 
 @observer
 export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -84,7 +83,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     }
 
     @computed get contents() {
-        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
+        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
             <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 47e79f088..f82c6d8a6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -46,6 +46,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
 import { Transform } from 'prosemirror-transform';
+import { CollectionPileView } from './CollectionPileView';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -67,7 +68,8 @@ export enum CollectionViewType {
     Carousel = "carousel",
     Linear = "linear",
     Staff = "staff",
-    Map = "map"
+    Map = "map",
+    Pile = "pileup"
 }
 
 export interface CollectionRenderProps {
@@ -169,6 +171,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />);
             case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />);
             case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
+            case CollectionViewType.Pile: { return (<CollectionPileView key="collview" {...props} />); }
             case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
             case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
             case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d8bd18ec7..9ca747113 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -13,10 +13,11 @@ import { listSpec } from "../../../../new_fields/Schema";
 
 export interface ViewDefBounds {
     type: string;
-    text?: string;
+    payload: any;
     x: number;
     y: number;
     z?: number;
+    text?: string;
     zIndex?: number;
     width?: number;
     height?: number;
@@ -24,14 +25,13 @@ export interface ViewDefBounds {
     fontSize?: number;
     highlight?: boolean;
     color?: string;
-    payload: any;
     replica?: string;
     pair?: { layout: Doc, data?: Doc };
 }
 
 export interface PoolData {
-    x?: number;
-    y?: number;
+    x: number;
+    y: number;
     z?: number;
     zIndex?: number;
     width?: number;
@@ -40,7 +40,7 @@ export interface PoolData {
     transition?: string;
     highlight?: boolean;
     replica?: string;
-    pair: { layout: Doc, data?: Doc }
+    pair: { layout: Doc, data?: Doc };
 }
 
 export interface ViewDefResult {
@@ -84,96 +84,88 @@ interface PivotColumn {
 export function computerPassLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
-    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
-    const docMap = new Map<string, ViewDefBounds>();
+    const docMap = new Map<string, PoolData>();
     childPairs.forEach(({ layout, data }, i) => {
         docMap.set(layout[Id], {
-            type: "doc",
             x: NumCast(layout.x),
             y: NumCast(layout.y),
             width: layout[WidthSym](),
             height: layout[HeightSym](),
-            payload: undefined,
             pair: { layout, data },
             replica: ""
         });
     });
-    return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+    return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
 }
 
 export function computerStarburstLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
-    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
-    const docMap = new Map<string, ViewDefBounds>();
+    const docMap = new Map<string, PoolData>();
     const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
     const docScale = NumCast(pivotDoc._starburstDocScale);
     const docSize = docScale * 100; // assume a icon sized at 100
     const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
-    childPairs.forEach((pair, i) => {
+    childPairs.forEach(({ layout, data }, i) => {
         const deg = i / childPairs.length * Math.PI * 2;
-        docMap.set(pair.layout[Id], {
-            type: "doc",
-            x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * pair.layout[WidthSym]() / 2,
-            y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * pair.layout[HeightSym]() / 2,
-            width: docScale * pair.layout[WidthSym](),
-            height: docScale * pair.layout[HeightSym](),
-            payload: undefined,
+        docMap.set(layout[Id], {
+            x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * layout[WidthSym]() / 2,
+            y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * layout[HeightSym]() / 2,
+            width: docScale * layout[WidthSym](),
+            height: docScale * layout[HeightSym](),
+            pair: { layout, data },
             replica: ""
         });
     });
-    return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
 }
 
 
 export function computePivotLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
-    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
-    const docMap = new Map<string, ViewDefBounds>();
+    const docMap = new Map<string, PoolData>();
     const fieldKey = "data";
     const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
 
     const pivotFieldKey = toLabel(pivotDoc._pivotField);
-    for (const doc of filterDocs) {
-        const lval = Cast(doc[pivotFieldKey], listSpec("string"), null);
-        const val = Field.toString(doc[pivotFieldKey] as Field);
+    childPairs.map(pair => {
+        const lval = Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+        const val = Field.toString(pair.layout[pivotFieldKey] as Field);
         if (lval) {
             lval.forEach((val, i) => {
                 !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
-                pivotColumnGroups.get(val)!.docs.push(doc);
+                pivotColumnGroups.get(val)!.docs.push(pair.layout);
                 pivotColumnGroups.get(val)!.replicas.push(i.toString());
             });
         } else if (val) {
             !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
-            pivotColumnGroups.get(val)!.docs.push(doc);
+            pivotColumnGroups.get(val)!.docs.push(pair.layout);
             pivotColumnGroups.get(val)!.replicas.push("");
         } else {
-            docMap.set(doc[Id], {
-                type: "doc",
+            docMap.set(pair.layout[Id], {
                 x: 0,
                 y: 0,
                 zIndex: -99,
                 width: 0,
                 height: 0,
-                payload: undefined,
-                pair: { layout: doc },
+                pair,
                 replica: ""
-            })
+            });
         }
-    }
+    });
     let nonNumbers = 0;
     childPairs.map(pair => {
         const num = toNumber(pair.layout[pivotFieldKey]);
@@ -252,12 +244,10 @@ export function computePivotLayout(
                 wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
             }
             docMap.set(doc[Id] + (val.replicas || ""), {
-                type: "doc",
                 x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
                 y: -y + (pivotAxisWidth - hgt) / 2,
                 width: wid,
                 height: hgt,
-                payload: undefined,
                 pair: { layout: doc },
                 replica: val.replicas[i]
             });
@@ -273,7 +263,7 @@ export function computePivotLayout(
     const dividers = sortedPivotKeys.map((key, i) =>
         ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
     groupNames.push(...dividers);
-    return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+    return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
 
 function toNumber(val: FieldResult<Field>) {
@@ -283,14 +273,13 @@ function toNumber(val: FieldResult<Field>) {
 export function computeTimelineLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
-    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
     const fieldKey = "data";
     const pivotDateGroups = new Map<number, Doc[]>();
-    const docMap = new Map<string, ViewDefBounds>();
+    const docMap = new Map<string, PoolData>();
     const groupNames: ViewDefBounds[] = [];
     const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
     const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
@@ -306,11 +295,11 @@ export function computeTimelineLayout(
 
     let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
     let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
-    filterDocs.map(doc => {
-        const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey])));
+    childPairs.forEach(pair => {
+        const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey])));
         if (!Number.isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
             !pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
-            pivotDateGroups.get(num)!.push(doc);
+            pivotDateGroups.get(num)!.push(pair.layout);
             minTime = Math.min(num, minTime);
             maxTime = Math.max(num, maxTime);
         }
@@ -369,7 +358,7 @@ export function computeTimelineLayout(
     }
 
     const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
-    return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childPairs.filter(c => !filterDocs.includes(c.layout)));
+    return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
         keyDocs.forEach(doc => {
@@ -382,13 +371,11 @@ export function computeTimelineLayout(
                 wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
             }
             docMap.set(doc[Id], {
-                type: "doc",
                 x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
                 zIndex: (curTime === key ? 1000 : zind++),
                 highlight: curTime === key,
                 width: wid / (Math.max(stack, 1)),
                 height: hgt / (Math.max(stack, 1)),
-                payload: undefined,
                 pair: { layout: doc },
                 replica: ""
             });
@@ -397,12 +384,19 @@ export function computeTimelineLayout(
     }
 }
 
-function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<string, ViewDefBounds>,
-    poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[],
-    extraDocs: { data?: Doc, layout: Doc }[]): ViewDefResult[] {
+function normalizeResults(
+    panelDim: number[],
+    fontHeight: number,
+    docMap: Map<string, PoolData>,
+    poolData: Map<string, PoolData>,
+    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
+    groupNames: ViewDefBounds[],
+    minWidth: number,
+    extras: ViewDefBounds[]
+): ViewDefResult[] {
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
-    const aggBounds = aggregateBounds(docEles.concat(grpEles).filter(e => e.zIndex !== -99), 0, 0);
+    const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0);
     aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
@@ -420,12 +414,11 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
                 zIndex: newPosRaw.zIndex,
                 width: (newPosRaw.width || 0) * scale,
                 height: newPosRaw.height! * scale,
-                pair: ele[1].pair!
+                pair: ele[1].pair
             };
             poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "transform 1s", ...newPos });
         }
     });
-    extraDocs.map(ed => poolData.set(ed.layout[Id], { x: 0, y: 0, zIndex: -99, pair: ed }));
 
     return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
         type: gname.type,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5555d9e84..af9d3c5be 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -902,7 +902,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
         const result = this.Document.arrangeScript?.script.run(params, console.log);
         if (result?.success) {
-            return { ...result, pair: params.pair, transition: "transform 1s" };
+            return { x: 0, y: 0, transition: "transform 1s", ...result, pair: params.pair };
         }
         const layoutDoc = Doc.Layout(params.pair.layout);
         const { x, y, z, color, zIndex } = params.pair.layout;
@@ -954,12 +954,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         engine: (
             poolData: Map<string, PoolData>,
             pivotDoc: Doc,
-            filterDocs: Doc[],
             childPairs: { layout: Doc, data?: Doc }[],
             panelDim: number[],
-            viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]) {
-        return engine(poolData, this.props.Document, this.childDocs,
-            this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+            viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]
+    ) {
+        return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
     }
 
     doFreeformLayout(poolData: Map<string, PoolData>) {
@@ -977,7 +976,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get doInternalLayoutComputation() {
         const newPool = new Map<string, PoolData>();
-        const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.();
+        const engine = this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
         switch (engine) {
             case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
             case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
@@ -1001,6 +1000,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements: ViewDefResult[] = computedElementData.slice();
+        const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
         Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
             elements.push({
                 ele: <CollectionFreeFormDocumentView
@@ -1009,8 +1009,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     replica={entry[1].replica}
                     dataProvider={this.childDataProvider}
                     LayoutDoc={this.childLayoutDocFunc}
-                    pointerEvents={this.props.viewDefDivClick ? false : undefined}
-                    jitterRotation={NumCast(this.props.Document.jitterRotation)}
+                    pointerEvents={this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true)) ?
+                        false : undefined}
+                    jitterRotation={NumCast(this.props.Document._jitterRotation)}
                     fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
                     FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
                 />,
@@ -1082,7 +1083,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" });
         optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
         // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" });
+        optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document._jitterRotation = (this.props.Document._jitterRotation ? 0 : 10)), icon: "paint-brush" });
         optionItems.push({
             description: "Import document", icon: "upload", event: ({ x, y }) => {
                 const input = document.createElement("input");
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8174ba1..cd8166309 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,12 +1,12 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCast, DataSym, WidthSym, HeightSym, Opt } from "../../../../new_fields/Doc";
 import { InkField, InkData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
 import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
 import { Cast, NumCast, FieldValue, StrCast } from "../../../../new_fields/Types";
 import { Utils } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
+import { Docs, DocUtils, DocumentOptions } from "../../../documents/Documents";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
@@ -310,11 +310,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = (selected: Doc[], asTemplate: boolean, isBackground?: boolean) => {
+    getCollection = (selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
         const bounds = this.Bounds;
         // const inkData = this.ink ? this.ink.inkData : undefined;
-        const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
-        const newCollection = creator(selected, {
+        const newCollection = (creator || Docs.Create.FreeformDocument)(selected, {
             x: bounds.left,
             y: bounds.top,
             _panX: 0,
@@ -338,8 +337,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const selected = this.marqueeSelect(false);
         SelectionManager.DeselectAll();
         selected.forEach(d => this.props.removeDocument(d));
-        const newCollection = this.getCollection(selected, false);
-        Doc.pileup(newCollection, selected);
+        const newCollection = Doc.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
         this.props.addDocument(newCollection);
         this.props.selectDocuments([newCollection], []);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -359,7 +357,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 return d;
             });
         }
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t");
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined);
         this.props.addDocument(newCollection);
         this.props.selectDocuments([newCollection], []);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -470,7 +468,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     }
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], false, true);
+        const newCollection = this.getCollection([], undefined, true);
         this.props.addDocument(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 18ffc38f5..337274774 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -976,7 +976,8 @@ export namespace Doc {
         if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
     }
 
-    export function pileup(newCollection: Doc, selected: Doc[]) {
+    export function pileup(selected: Doc[], x: number, y: number) {
+        const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
         let w = 0, h = 0;
         selected.forEach((d, i) => {
             Doc.iconify(d);
@@ -993,12 +994,10 @@ export namespace Doc {
         newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
         newCollection._width = newCollection._height = 110;
         //newCollection.borderRounding = "40px";
-        newCollection.jitterRotation = 10;
+        newCollection._jitterRotation = 10;
         newCollection._backgroundColor = "gray";
-
-        newCollection._layoutEngine = "pass";
         newCollection.overflow = "visible";
-        newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self, this)", { self: Doc.name, this: Doc.name });
+        return newCollection;
     }
 
 
@@ -1055,28 +1054,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
     return docs.length ? new List(docs) : prevValue;
 });
 Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
-Scripting.addGlobal(function toggleStarburst(self: Doc, tLayout: Doc) {
-    if (self._layoutEngine === 'starburst') {
-        const defaultSize = 110;
-        tLayout.overflow = undefined;
-        self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - NumCast(tLayout._starburstPileWidth, defaultSize) / 2;
-        self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - NumCast(tLayout._starburstPileHeight, defaultSize) / 2;
-        tLayout._width = NumCast(tLayout._starburstPileWidth, defaultSize);
-        tLayout._height = NumCast(tLayout._starburstPileHeight, defaultSize);
-        self._layoutEngine = 'pass';
-    } else {
-        const defaultSize = 25;
-        tLayout.overflow = 'visible';
-        !tLayout._starburstRadius && (tLayout._starburstRadius = 500);
-        !tLayout._starburstDocScale && (tLayout._starburstDocScale = 2.5);
-        if (self._layoutEngine === 'pass') {
-            self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - defaultSize / 2;
-            self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - defaultSize / 2;
-            tLayout._starburstPileWidth = tLayout[WidthSym]();
-            tLayout._starburstPileHeight = tLayout[HeightSym]();
-        }
-        tLayout._width = tLayout._height = defaultSize;
-        self._layoutEngine = 'starburst';
-    }
-});
\ No newline at end of file
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

