From c23d9ba83c87511a626bfe8d1da5dd981e311262 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 23 Jan 2020 18:24:38 -0500
Subject: got rid of extension docs.  changed layout-specific keys to start
 with "_" which flags them to be written to the current layout document

---
 src/client/views/MetadataEntryMenu.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 243cdb8f6..a48e8f508 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -178,11 +178,11 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
         }
         docSource = docSource as Doc[] | Doc;
         if (docSource instanceof Doc) {
-            if (docSource.viewType === undefined) {
+            if (docSource._viewType === undefined) {
                 return (null);
             }
         } else if (Array.isArray(docSource)) {
-            if (!docSource.every(doc => doc.viewType !== undefined)) {
+            if (!docSource.every(doc => doc._viewType !== undefined)) {
                 return null;
             }
         }
-- 
cgit v1.2.3-70-g09d2


From 267bab4c2935841e6e94c63b982af9901ad89cbc Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 28 Jan 2020 11:11:18 -0500
Subject: multicolumn drop fixes. adding menu entry to stacking columndocs. 
 fixed imagebox nativewidth/height settings.

---
 src/client/views/EditableView.tsx                  | 20 +++++----
 src/client/views/MetadataEntryMenu.tsx             |  1 -
 src/client/views/TemplateMenu.scss                 |  1 +
 .../CollectionStackingViewFieldColumn.tsx          | 49 +++++++++++++++-------
 src/client/views/collections/CollectionView.tsx    |  6 +--
 .../views/collections/CollectionViewChromes.tsx    |  5 ++-
 .../CollectionMulticolumnView.tsx                  | 20 +++++++--
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 8 files changed, 69 insertions(+), 35 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 394caba72..780c5b2f4 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,11 +1,12 @@
 import React = require('react');
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { observable, action, trace } from 'mobx';
-import "./EditableView.scss";
 import * as Autosuggest from 'react-autosuggest';
-import { undoBatch } from '../util/UndoManager';
-import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
 import { ObjectField } from '../../new_fields/ObjectField';
+import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
+import { ContextMenu } from './ContextMenu';
+import { ContextMenuProps } from './ContextMenuItem';
+import "./EditableView.scss";
 
 export interface EditableProps {
     /**
@@ -44,6 +45,7 @@ export interface EditableProps {
     editing?: boolean;
     onClick?: (e: React.MouseEvent) => boolean;
     isEditingCallback?: (isEditing: boolean) => void;
+    menuCallback?: (x: number, y: number) => void;
     HeadingObject?: SchemaHeaderField | undefined;
     HeadingsHack?: number;
     toggle?: () => void;
@@ -88,12 +90,14 @@ export class EditableView extends React.Component<EditableProps> {
             } else if (this.props.OnFillDown) {
                 this.props.OnFillDown(e.currentTarget.value);
                 this._editing = false;
-                this.props.isEditingCallback && this.props.isEditingCallback(false);
+                this.props.isEditingCallback?.(false);
             }
         } else if (e.key === "Escape") {
             e.stopPropagation();
             this._editing = false;
-            this.props.isEditingCallback && this.props.isEditingCallback(false);
+            this.props.isEditingCallback?.(false);
+        } else if (e.key === ":") {
+            this.props.menuCallback?.(e.currentTarget.offsetLeft, e.currentTarget.offsetTop);
         }
     }
 
@@ -102,7 +106,7 @@ export class EditableView extends React.Component<EditableProps> {
         e.nativeEvent.stopPropagation();
         if (!this.props.onClick || !this.props.onClick(e)) {
             this._editing = true;
-            this.props.isEditingCallback && this.props.isEditingCallback(true);
+            this.props.isEditingCallback?.(true);
         }
         e.stopPropagation();
     }
@@ -111,7 +115,7 @@ export class EditableView extends React.Component<EditableProps> {
     private finalizeEdit(value: string, shiftDown: boolean) {
         this._editing = false;
         if (this.props.SetValue(value, shiftDown)) {
-            this.props.isEditingCallback && this.props.isEditingCallback(false);
+            this.props.isEditingCallback?.(false);
         }
     }
 
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index a48e8f508..22259e509 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -19,7 +19,6 @@ export interface MetadataEntryProps {
 export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     @observable private _currentKey: string = "";
     @observable private _currentValue: string = "";
-    @observable private suggestions: string[] = [];
     private _addChildren: boolean = false;
     @observable _allSuggestions: string[] = [];
     _suggestionDispser: IReactionDisposer | undefined;
diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss
index 858fcfb5f..bbed8cd96 100644
--- a/src/client/views/TemplateMenu.scss
+++ b/src/client/views/TemplateMenu.scss
@@ -42,6 +42,7 @@
 
     .templateToggle, .chromeToggle {
         text-align: left;
+        color: black;
     }
 
     input {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index dabc14445..6a6f7aa6d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -21,6 +21,9 @@ import { TraceMobx } from "../../../new_fields/util";
 import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageField } from "../../../new_fields/URLField";
 import { ImageBox } from "../nodes/ImageBox";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { RichTextField } from "../../../new_fields/RichTextField";
 
 library.add(faPalette);
 
@@ -135,21 +138,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     addDocument = (value: string, shiftDown?: boolean) => {
-        if (value === ":freeForm") {
-            return this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200 }));
-        } else if (value.startsWith(":")) {
-            const { Document, addDocument } = this.props.parent.props;
-            const fieldKey = value.substring(1);
-            const dataDoc = this.props.parent.props.DataDoc || this.props.parent.Document;
-            const created = Docs.Get.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(Document));
-            if (created) {
-                if (this.props.parent.Document.isTemplateDoc) {
-                    Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
-                }
-                return addDocument(created);
-            }
-            return false;
-        }
+        if (!value) return false;
         this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document.sectionFilter);
         const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
@@ -275,6 +264,34 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @observable _headingsHack: number = 1;
 
+    menuCallback = (x: number, y: number) => {
+        ContextMenu.Instance.clearItems();
+        const layoutItems: ContextMenuProps[] = [];
+        const docItems: ContextMenuProps[] = [];
+
+        const dataDoc = this.props.parent.props.DataDoc || this.props.parent.Document;
+        Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField).map(fieldKey =>
+            docItems.push({
+                description: ":" + fieldKey, event: () => {
+                    const created = Docs.Get.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.parent.props.Document));
+                    if (created) {
+                        if (this.props.parent.Document.isTemplateDoc) {
+                            Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
+                        }
+                        return this.props.parent.props.addDocument(created);
+                    }
+                }, icon: "compress-arrows-alt"
+            }));
+        layoutItems.push({ description: ":freeform", event: () => this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
+        layoutItems.push({ description: ":columns", event: () => this.props.parent.props.addDocument(Docs.Create.MulticolumnDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
+        layoutItems.push({ description: ":image", event: () => this.props.parent.props.addDocument(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
+
+        ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
+        const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y);
+        ContextMenu.Instance.displayMenu(pt[0], pt[1]);
+    }
+
     render() {
         TraceMobx();
         const cols = this.props.cols();
@@ -375,7 +392,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                             {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
                                 <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
                                     style={{ width: style.columnWidth / style.numGroupColumns }}>
-                                    <EditableView {...newEditableViewProps} />
+                                    <EditableView {...newEditableViewProps} menuCallback={this.menuCallback} />
                                 </div> : null}
                         </div>
                 }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0910e6d40..dab0ce08e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -48,11 +48,11 @@ export enum CollectionViewType {
     Tree,
     Stacking,
     Masonry,
+    Multicolumn,
     Pivot,
-    Linear,
     Carousel,
+    Linear,
     Staff,
-    Multicolumn,
     Timeline
 }
 
@@ -65,10 +65,10 @@ export namespace CollectionViewType {
         ["tree", CollectionViewType.Tree],
         ["stacking", CollectionViewType.Stacking],
         ["masonry", CollectionViewType.Masonry],
+        ["multicolumn", CollectionViewType.Multicolumn],
         ["pivot", CollectionViewType.Pivot],
         ["carousel", CollectionViewType.Carousel],
         ["linear", CollectionViewType.Linear],
-        ["multicolumn", CollectionViewType.Multicolumn]
     ]);
 
     export const valueOf = (value: string) => stringMapping.get(value.toLowerCase());
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index d3ac86070..e09f90e2c 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -404,9 +404,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree View</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Pivot View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Linear View</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Multicolumn View</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Pivot View</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Carousel View</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Linear View</option>
                         </select>
                         <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}>
                             <input className="collectionViewBaseChrome-viewSpecsInput"
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b9b367106..87b2a9729 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -12,6 +12,8 @@ import { computed, trace, observable, action } from 'mobx';
 import { Transform } from '../../../util/Transform';
 import WidthLabel from './MulticolumnWidthLabel';
 import ResizeBar from './MulticolumnResizer';
+import { undoBatch } from '../../../util/UndoManager';
+import { DragManager } from '../../../util/DragManager';
 
 type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
 const MulticolumnDocument = makeInterface(documentSchema);
@@ -186,6 +188,19 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         return Transform.Identity(); // type coersion, this case should never be hit
     }
 
+    @undoBatch
+    @action
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (super.drop(e, de)) {
+            de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
+                d.widthUnit = "*";
+                d.widthMagnitude = 1;
+            }));
+        }
+        return false;
+    }
+
+
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
     /**
@@ -234,10 +249,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
 
     render(): JSX.Element {
         return (
-            <div
-                className={"collectionMulticolumnView_contents"}
-                ref={this.createDropTarget}
-            >
+            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget} b >
                 {this.contents}
             </div>
         );
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ffcbe459e..33c694c6e 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -257,7 +257,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
             })
                 .catch((err: any) => console.log(err));
         } else if (this.Document._nativeHeight !== cachedNativeSize.width || this.Document._nativeWidth !== cachedNativeSize.height) {
-            setTimeout(() => {
+            !(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc) && setTimeout(() => {
                 this.Document._nativeWidth = cachedNativeSize.width;
                 this.Document._nativeHeight = cachedNativeSize.height;
             }, 0);
-- 
cgit v1.2.3-70-g09d2


From ac9492714eb9264b67d8033d8753fc6ed51b4e29 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 29 Jan 2020 13:54:27 -0500
Subject: ui cleanup for pivot viewer/chrome.  error fixes.  fixed layout of
 flyouts

---
 solr-8.3.1/server/solr/dash/core.properties        |   2 +-
 src/client/util/ClientUtils.ts.temp                |   3 -
 src/client/util/RichTextSchema.tsx                 |   2 +-
 src/client/views/DocumentButtonBar.tsx             | 100 +++++++++++++++++----
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/MetadataEntryMenu.scss            |   4 +
 src/client/views/MetadataEntryMenu.tsx             |   2 +-
 src/client/views/TemplateMenu.tsx                  |  66 +-------------
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 .../views/collections/CollectionPivotView.tsx      |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   6 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 .../views/collections/CollectionViewChromes.scss   |  35 +++++++-
 .../views/collections/CollectionViewChromes.tsx    |  94 ++++++++++---------
 .../views/collections/ParentDocumentSelector.tsx   |  11 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 .../CollectionMulticolumnView.tsx                  |   2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   4 +-
 src/client/views/nodes/PresBox.tsx                 |   4 +-
 src/client/views/search/SearchBox.scss             |   1 +
 src/new_fields/Doc.ts                              |  11 +--
 .../authentication/models/current_user_utils.ts    |   5 +-
 22 files changed, 209 insertions(+), 159 deletions(-)
 delete mode 100644 src/client/util/ClientUtils.ts.temp

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

diff --git a/solr-8.3.1/server/solr/dash/core.properties b/solr-8.3.1/server/solr/dash/core.properties
index 0912a8303..70081ebc6 100644
--- a/solr-8.3.1/server/solr/dash/core.properties
+++ b/solr-8.3.1/server/solr/dash/core.properties
@@ -1,5 +1,5 @@
 #Written by CorePropertiesLocator
-#Wed Jan 08 14:44:34 UTC 2020
+#Wed Jan 29 14:30:24 UTC 2020
 name=dash
 config=solrconfig.xml
 schema=schema.xml
diff --git a/src/client/util/ClientUtils.ts.temp b/src/client/util/ClientUtils.ts.temp
deleted file mode 100644
index f9fad5ed9..000000000
--- a/src/client/util/ClientUtils.ts.temp
+++ /dev/null
@@ -1,3 +0,0 @@
-export namespace ClientUtils {
-    export const RELEASE = "mode";
-}
\ No newline at end of file
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 8c9e5e263..f14e09a40 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -695,7 +695,7 @@ export class DashDocCommentView {
             e.stopPropagation();
         };
         this._collapsed.onpointerenter = (e: any) => {
-            DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+            DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
             e.preventDefault();
             e.stopPropagation();
         };
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 4441356dc..6abb7108f 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,27 +1,28 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, runInAction, computed } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Id } from '../../new_fields/FieldSymbols';
 import { RichTextField } from '../../new_fields/RichTextField';
-import { NumCast, StrCast, Cast } from "../../new_fields/Types";
+import { NumCast, StrCast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { DragManager } from "../util/DragManager";
+import RichTextMenu from '../util/RichTextMenu';
 import { UndoManager } from "../util/UndoManager";
-import './DocumentButtonBar.scss';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { ParentDocSelector } from './collections/ParentDocumentSelector';
 import './collections/ParentDocumentSelector.scss';
+import './DocumentButtonBar.scss';
 import { LinkMenu } from "./linking/LinkMenu";
-import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
+import { DocumentView } from './nodes/DocumentView';
+import { GoogleRef } from "./nodes/FormattedTextBox";
 import { TemplateMenu } from "./TemplateMenu";
 import { Template, Templates } from "./Templates";
 import React = require("react");
-import { DocumentView } from './nodes/DocumentView';
-import { ParentDocSelector } from './collections/ParentDocumentSelector';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import RichTextMenu from '../util/RichTextMenu';
-import { Id } from '../../new_fields/FieldSymbols';
+import { DragManager } from '../util/DragManager';
+import { MetadataEntryMenu } from './MetadataEntryMenu';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -43,6 +44,7 @@ const fetch: IconProp = "sync-alt";
 @observer
 export class DocumentButtonBar extends React.Component<{ views: (DocumentView | undefined)[], stack?: any }, {}> {
     private _linkButton = React.createRef<HTMLDivElement>();
+    private _dragRef = React.createRef<HTMLDivElement>();
     private _downX = 0;
     private _downY = 0;
     private _pullAnimating = false;
@@ -201,7 +203,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         const view0 = this.view0;
         const linkCount = view0 && DocListCast(view0.props.Document.links).length;
         return !view0 ? (null) : <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
-            <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
+            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={<LinkMenu docView={view0} addDocTab={view0.props.addDocTab} changeFlyout={emptyFunction} />}>
                 <div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
                     {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
@@ -210,6 +212,19 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         </div>;
     }
 
+    @computed
+    get metadataButton() {
+        const view0 = this.view0;
+        return !view0 ? (null) : <div title="Show metadata panel" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+                content={<MetadataEntryMenu docs={() => this.props.views.filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
+                <div className={"documentButtonBar-linkButton-" + "empty"} >
+                    {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" />}
+                </div>
+            </Flyout>
+        </div>;
+    }
+
     @computed
     get contextButton() {
         return !this.view0 ? (null) : <ParentDocSelector Views={this.props.views.filter(v => v).map(v => v as DocumentView)} Document={this.view0.props.Document} addDocTab={(doc, data, where) => {
@@ -220,11 +235,61 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         }} />;
     }
 
-    render() {
-        if (!this.view0) return (null);
+    private _downx = 0;
+    private _downy = 0;
+    onAliasButtonUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onAliasButtonMoved);
+        document.removeEventListener("pointerup", this.onAliasButtonUp);
+        e.stopPropagation();
+    }
+
+    onAliasButtonDown = (e: React.PointerEvent): void => {
+        this._downx = e.clientX;
+        this._downy = e.clientY;
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.onAliasButtonMoved);
+        document.addEventListener("pointermove", this.onAliasButtonMoved);
+        document.removeEventListener("pointerup", this.onAliasButtonUp);
+        document.addEventListener("pointerup", this.onAliasButtonUp);
+    }
+    onAliasButtonMoved = (e: PointerEvent): void => {
+        if (this._dragRef.current !== null && (Math.abs(e.clientX - this._downx) > 4 || Math.abs(e.clientY - this._downy) > 4)) {
+            document.removeEventListener("pointermove", this.onAliasButtonMoved);
+            document.removeEventListener("pointerup", this.onAliasButtonUp);
+
+            const dragDocView = this.props.views[0]!;
+            const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
+            const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+            dragData.embedDoc = true;
+            dragData.dropAction = "alias";
+            DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
+                offsetX: dragData.offset[0],
+                offsetY: dragData.offset[1],
+                hideSource: false
+            });
+        }
+        e.stopPropagation();
+    }
+
+    @computed
+    get templateButton() {
+        const view0 = this.view0;
         const templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
             templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
+        return !view0 ? (null) : <div title="Customize layout" className="documentButtonBar-linkFlyout" ref={this._dragRef}>
+            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+                content={<TemplateMenu docs={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
+                <div className={"documentButtonBar-linkButton-" + "empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
+                    {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
+                </div>
+            </Flyout>
+        </div>;
+    }
+
+    render() {
+        if (!this.view0) return (null);
 
         const isText = this.view0.props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field.  need to add fieldKey to DocumentView
         const considerPull = isText && this.considerGoogleDocsPull;
@@ -234,7 +299,13 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
                 {this.linkButton}
             </div>
             <div className="documentButtonBar-button">
-                <TemplateMenu docs={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />
+                {this.templateButton}
+            </div>
+            <div className="documentButtonBar-button">
+                {this.metadataButton}
+            </div>
+            <div className="documentButtonBar-button">
+                {this.contextButton}
             </div>
             <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
                 {this.considerGoogleDocsPush}
@@ -242,7 +313,6 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
             <div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
                 {this.considerGoogleDocsPull}
             </div>
-            {this.contextButton}
         </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2297c2bb3..87a81504c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import {
-    faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight,
+    faArrowDown, faBullseye, faFilter, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight,
     faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp, faClipboard
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -131,6 +131,8 @@ export class MainView extends React.Component {
         library.add(faLongArrowAltRight);
         library.add(faCheck);
         library.add(faCaretUp);
+        library.add(faFilter);
+        library.add(faBullseye);
         library.add(faArrowDown);
         library.add(faArrowUp);
         library.add(faCloudUploadAlt);
diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss
index 7da55fd1c..5f4a52c0c 100644
--- a/src/client/views/MetadataEntryMenu.scss
+++ b/src/client/views/MetadataEntryMenu.scss
@@ -8,6 +8,10 @@
     }
 }
 
+#metadataEntry-outer {
+    overflow: auto !important;
+}
+
 .metadataEntry-keys {
     max-height: 80;
     overflow-y: auto; 
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 22259e509..23b21ae0c 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -196,7 +196,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     _ref = React.createRef<HTMLInputElement>();
     render() {
         return (
-            <div className="metadataEntry-outerDiv">
+            <div className="metadataEntry-outerDiv" id="metadataEntry-outer">
                 <div className="metadataEntry-inputArea">
                     Key:
                     <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 92987bde0..f2ae93b78 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -51,9 +51,6 @@ export interface TemplateMenuProps {
 @observer
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @observable private _hidden: boolean = true;
-    private _downx = 0;
-    private _downy = 0;
-    private _dragRef = React.createRef<HTMLUListElement>();
 
     toggleCustom = (e: React.ChangeEvent<HTMLInputElement>): void => {
         this.props.docs.map(dv => dv.setCustomView(e.target.checked));
@@ -81,19 +78,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         }
     }
 
-    @undoBatch
-    @action
-    clearTemplates = (event: React.MouseEvent) => {
-        Templates.TemplateList.forEach(template => this.props.docs.forEach(d => d.Document["show" + template.Name] = undefined));
-        ["backgroundColor", "borderRounding", "width", "height"].forEach(field => this.props.docs.forEach(d => {
-            if (d.Document.isTemplateDoc && d.props.DataDoc) {
-                d.Document[field] = undefined;
-            } else if (d.Document["default" + field[0].toUpperCase() + field.slice(1)] !== undefined) {
-                d.Document[field] = Doc.GetProto(d.Document)[field] = undefined;
-            }
-        }));
-    }
-
     @action
     toggleTemplateActivity = (): void => {
         this._hidden = !this._hidden;
@@ -107,40 +91,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : "enabled");
         });
     }
-    onAliasButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onAliasButtonMoved);
-        document.removeEventListener("pointerup", this.onAliasButtonUp);
-        e.stopPropagation();
-    }
-
-    onAliasButtonDown = (e: React.PointerEvent): void => {
-        this._downx = e.clientX;
-        this._downy = e.clientY;
-        e.stopPropagation();
-        e.preventDefault();
-        document.removeEventListener("pointermove", this.onAliasButtonMoved);
-        document.addEventListener("pointermove", this.onAliasButtonMoved);
-        document.removeEventListener("pointerup", this.onAliasButtonUp);
-        document.addEventListener("pointerup", this.onAliasButtonUp);
-    }
-    onAliasButtonMoved = (e: PointerEvent): void => {
-        if (this._dragRef.current !== null && (Math.abs(e.clientX - this._downx) > 4 || Math.abs(e.clientY - this._downy) > 4)) {
-            document.removeEventListener("pointermove", this.onAliasButtonMoved);
-            document.removeEventListener("pointerup", this.onAliasButtonUp);
-
-            const dragDocView = this.props.docs[0];
-            const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
-            const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            dragData.embedDoc = true;
-            dragData.dropAction = "alias";
-            DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
-                offsetX: dragData.offset[0],
-                offsetY: dragData.offset[1],
-                hideSource: false
-            });
-        }
-        e.stopPropagation();
-    }
 
     render() {
         const layout = Doc.Layout(this.props.docs[0].Document);
@@ -151,18 +101,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         templateMenu.push(<OtherToggle key={"custom"} name={"Custom"} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") !== "layout"} toggle={this.toggleCustom} />);
         templateMenu.push(<OtherToggle key={"narrative"} name={"Narrative"} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") === "layout_narrative"} toggle={this.toggleNarrative} />);
         templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
-        return (
-            <div className="templating-button" onPointerDown={this.onAliasButtonDown} title="Drag:(create alias). Tap:(modify layout)." >
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                    content={<ul className="template-list" ref={this._dragRef} style={{ display: "block" }}>
-                        {templateMenu}
-                        {<button onClick={this.clearTemplates}>Restore Defaults</button>}
-                    </ul>}>
-                    <span className="parentDocumentSelector-button" >
-                        <FontAwesomeIcon icon={faEdit} size={"sm"} />
-                    </span>
-                </Flyout>
-            </div>
-        );
+        return <ul className="template-list" style={{ display: "block" }}>
+            {templateMenu}
+        </ul>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 160279efd..82cb3bc88 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -645,7 +645,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         if (curPres) {
             const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
             Doc.GetProto(pinDoc).presentationTargetDoc = doc;
-            Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
+            Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title?.toString()');
             const data = Cast(curPres.data, listSpec(Doc));
             if (data) {
                 data.push(pinDoc);
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 6de20593b..440b6856b 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -110,7 +110,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) {
             </div>
         );
         return !facetCollection ? (null) :
-            <div className="collectionPivotView">
+            <div className="collectionPivotView" style={{ height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
                 <div className={"pivotKeyEntry"}>
                     <EditableView
                         contents={this.props.Document.pivotField}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 65d421c52..8679c8bd1 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -52,9 +52,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
         private _childLayoutDisposer?: IReactionDisposer;
         protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
-            this.dropDisposer && this.dropDisposer();
-            this.gestureDisposer && this.gestureDisposer();
-            this.multiTouchDisposer && this.multiTouchDisposer();
+            this.dropDisposer?.();
+            this.gestureDisposer?.();
+            this.multiTouchDisposer?.();
             if (ele) {
                 this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 395eed372..a7733ab5f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -645,10 +645,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                 const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
                 const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "short_description" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] },  { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] }  ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`;
 
+                const textDoc = TextDocument("", { title: "details", _autoHeight: true });
                 const detailedLayout = Docs.Create.StackingDocument([
                     CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }),
-                    TextDocument("", { title: "details", _autoHeight: true, _textTemplate: new RichTextField(detailedTemplate, "short_description year company") })
+                    textDoc,
                 ], { _chromeStatus: "disabled", title: "detailed layout stack" });
+                textDoc.data = new RichTextField(detailedTemplate, "short_description year company");
                 detailedLayout.isTemplateDoc = makeTemplate(detailedLayout);
 
                 const cardLayout = ImageDocument(fallbackImg, { title: "cardLayout", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 64411b5fe..efe185922 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -20,7 +20,7 @@
 
             .collectionViewBaseChrome-viewPicker {
                 font-size: 75%;
-                text-transform: uppercase;
+                //text-transform: uppercase;
                 letter-spacing: 2px;
                 background: rgb(238, 238, 238);
                 color: grey;
@@ -34,6 +34,26 @@
                 outline-color: black;
             }
 
+            .collectionViewBaseChrome-cmdPicker {
+                margin-left: 3px;
+                margin-right: 0px;
+                background: rgb(238, 238, 238);
+                border: none;
+                color: grey;
+            }
+            .commandEntry-outerDiv {
+                pointer-events: all;
+                background-color: gray;
+                display: flex;
+                flex-direction: row;
+                .commandEntry-drop {
+                    color:white;
+                    width:25px;
+                    margin-top: auto;
+                    margin-bottom: auto;
+                }
+            }
+
             .collectionViewBaseChrome-collapse {
                 transition: all .5s, opacity 0.3s;
                 position: absolute;
@@ -53,6 +73,18 @@
             .collectionViewBaseChrome-viewSpecs {
                 margin-left: 10px;
                 display: grid;
+                
+                .collectionViewBaseChrome-filterIcon {
+                    position: relative;
+                    display: flex;
+                    margin: auto;
+                    background: gray;
+                    color: white;
+                    width: 40px;
+                    height: 40px;
+                    align-items: center;
+                    justify-content: center;
+                }
 
                 .collectionViewBaseChrome-viewSpecsInput {
                     padding: 12px 10px 11px 10px;
@@ -240,7 +272,6 @@
 .commandEntry-outerDiv {
     display: flex;
     flex-direction: column;
-    width: 165px;
     height: 40px;
 }
 .commandEntry-inputArea {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index e09f90e2c..d1919d588 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -39,25 +39,24 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
 
     _templateCommand = {
-        title: "set template", script: "setChildLayout(this.target, this.source?.[0])", params: ["target", "source"],
+        title: "=> item view", script: "setChildLayout(this.target, this.source?.[0])", params: ["target", "source"],
         initialize: emptyFunction,
         immediate: (draggedDocs: Doc[]) => Doc.setChildLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined)
     };
     _narrativeCommand = {
-        title: "set detailed template", script: "setChildLayout(this.target, this.source?.[0])", params: ["target", "source"],
+        title: "=> click item view", script: "setChildDetailedLayout(this.target, this.source?.[0])", params: ["target", "source"],
         initialize: emptyFunction,
-        immediate: (draggedDocs: Doc[]) => Doc.setChildDetailed(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined)
+        immediate: (draggedDocs: Doc[]) => Doc.setChildDetailedLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined)
     };
     _contentCommand = {
-        // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"],  // bcz: doesn't look like we can do async stuff in scripting...
-        title: "set content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"],
+        title: "=> content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"],
         initialize: emptyFunction,
         immediate: (draggedDocs: Doc[]) => Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(draggedDocs.map((d: any) => Doc.MakeAlias(d)))
     };
     _viewCommand = {
-        title: "restore view", script: "this.target._panX = this.restoredPanX; this.target._panY = this.restoredPanY; this.target.scale = this.restoredScale;", params: ["target"],
+        title: "=> saved view", script: "this.target._panX = this.restoredPanX; this.target._panY = this.restoredPanY; this.target.scale = this.restoredScale;", params: ["target"],
+        initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document._panX; button.restoredPanY = this.props.CollectionView.props.Document._panY; button.restoredScale = this.props.CollectionView.props.Document.scale; },
         immediate: (draggedDocs: Doc[]) => { this.props.CollectionView.props.Document._panX = 0; this.props.CollectionView.props.Document._panY = 0; this.props.CollectionView.props.Document.scale = 1; },
-        initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document._panX; button.restoredPanY = this.props.CollectionView.props.Document._panY; button.restoredScale = this.props.CollectionView.props.Document.scale; }
     };
     _freeform_commands = [this._contentCommand, this._templateCommand, this._narrativeCommand, this._viewCommand];
     _stacking_commands = [this._contentCommand, this._templateCommand];
@@ -153,21 +152,32 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         this.props.CollectionView.props.Document._viewType = parseInt(e.target.selectedOptions[0].value);
     }
 
+    commandChanged = (e: React.ChangeEvent) => {
+        //@ts-ignore
+        runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
+    }
+
     @action
     openViewSpecs = (e: React.SyntheticEvent) => {
-        this._viewSpecsOpen = true;
+        if (this._viewSpecsOpen) this.closeViewSpecs();
+        else {
+            this._viewSpecsOpen = true;
 
-        //@ts-ignore
-        if (!e.target.classList[0].startsWith("qs")) {
-            this.closeDatePicker();
-        }
+            //@ts-ignore
+            if (!e.target?.classList[0]?.startsWith("qs")) {
+                this.closeDatePicker();
+            }
 
-        e.stopPropagation();
-        document.removeEventListener("pointerdown", this.closeViewSpecs);
-        document.addEventListener("pointerdown", this.closeViewSpecs);
+            e.stopPropagation();
+            document.removeEventListener("pointerdown", this.closeViewSpecs);
+            document.addEventListener("pointerdown", this.closeViewSpecs);
+        }
     }
 
-    @action closeViewSpecs = () => { this._viewSpecsOpen = false; document.removeEventListener("pointerdown", this.closeViewSpecs); };
+    @action closeViewSpecs = () => {
+        this._viewSpecsOpen = false;
+        document.removeEventListener("pointerdown", this.closeViewSpecs);
+    };
 
     @action
     openDatePicker = (e: React.PointerEvent) => {
@@ -399,23 +409,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             onPointerDown={stopPropagation}
                             onChange={this.viewChanged}
                             value={NumCast(this.props.CollectionView.props.Document._viewType)}>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Multicolumn View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Pivot View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Carousel View</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Linear View</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">schema</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Multicolumn</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Pivot</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Carousel</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Linear</option>
                         </select>
-                        <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}>
-                            <input className="collectionViewBaseChrome-viewSpecsInput"
-                                placeholder="FILTER"
-                                value={this.filterValue ? this.filterValue.script.originalScript === "return true" ? "" : this.filterValue.script.originalScript : ""}
-                                onChange={(e) => { }}
-                                onPointerDown={this.openViewSpecs} />
-                            {this.getPivotInput()}
+                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
+                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
+                                <FontAwesomeIcon icon="filter" size="2x" />
+                            </div>
                             <div className="collectionViewBaseChrome-viewSpecsMenu"
                                 onPointerDown={this.openViewSpecs}
                                 style={{
@@ -456,17 +463,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             </div>
                         </div>
                         <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
-                            <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
-                                <div className="commandEntry-inputArea" onPointerDown={this.autoSuggestDown} >
-                                    <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
-                                        getSuggestionValue={this.getSuggestionValue}
-                                        suggestions={this.suggestions}
-                                        alwaysRenderSuggestions={true}
-                                        renderSuggestion={this.renderSuggestion}
-                                        onSuggestionsFetchRequested={this.onSuggestionFetch}
-                                        onSuggestionsClearRequested={this.onSuggestionClear}
-                                        ref={this._autosuggestRef} />
+                            <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
+                                <div className="commandEntry-drop">
+                                    <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
                                 </div>
+                                <select
+                                    className="collectionViewBaseChrome-cmdPicker"
+                                    onPointerDown={stopPropagation}
+                                    onChange={this.commandChanged}
+                                    value={this._currentKey}>
+                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""}>{""}</option>
+                                    {this._buttonizableCommands.map(cmd =>
+                                        <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
+                                    )}
+                                </select>
                             </div>
                         </div>
                     </div>
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 18f1baf44..115f8d633 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -64,22 +64,13 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
             this.props.addDocTab(col, undefined, "inTab"); // bcz: dataDoc?
         };
     }
-    get metadataMenu() {
-        return <div className="parentDocumentSelector-metadata">
-            <Flyout anchorPoint={anchorPoints.TOP_LEFT}
-                content={<MetadataEntryMenu docs={() => this.props.Views.map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
-                <div className="docDecs-tagButton" title="Add fields"><FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" /></div>
-            </Flyout>
-        </div>;
-    }
 
     render() {
         return <div >
-            <div key="metadata">Metadata: {this.metadataMenu}</div>
             <p key="contexts">Contexts:</p>
             {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title?.toString()}</a></p>)}
             {this._otherDocs.length ? <hr key="hr" /> : null}
-            {this._otherDocs.map(doc => <p key="p"><a onClick={this.getOnClick(doc)}>{doc.col.title?.toString()}</a></p>)}
+            {this._otherDocs.map(doc => <p key={"p" + doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title?.toString()}</a></p>)}
         </div>;
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bac968165..20d20f1f6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -653,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             if (!init) {
                 state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY };
                 HistoryUtil.pushState(state);
-            } else if (init.panX !== this.Document.panX || init.panY !== this.Document.panY) {
+            } else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) {
                 init.panX = this.Document._panX;
                 init.panY = this.Document._panY;
                 HistoryUtil.pushState(state);
@@ -899,7 +899,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     onContextMenu = (e: React.MouseEvent) => {
         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: "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" });
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 87b2a9729..041eb69da 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -249,7 +249,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget} b >
+            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget}>
                 {this.contents}
             </div>
         );
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3244b7d3e..2183129cf 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -60,8 +60,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
     componentWillUnmount() { this._disposer?.(); }
     componentDidMount() {
-        this._disposer = reaction(() => Array.from(Cast(this.props.Document?.animateToPos, listSpec("number"), null)),
-            target => this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] :
+        this._disposer = reaction(() => Array.from(Cast(this.props.Document?.animateToPos, listSpec("number"), null) || []),
+            target => this._animPos = !target || !target?.length ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] :
                 this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]),
             { fireImmediately: true });
     }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index eda12d6fb..428e9aa7b 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -44,7 +44,7 @@ export class PresBox extends React.Component<FieldViewProps> {
                     if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) {
                         const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" });
                         Doc.GetProto(pinDoc).presentationTargetDoc = item;
-                        Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()');
+                        Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('this.presentationTargetDoc?.title?.toString()');
                         value.splice(i, 1, pinDoc);
                     }
                 });
@@ -342,7 +342,7 @@ export class PresBox extends React.Component<FieldViewProps> {
             doc.presBox = this.props.Document;
             doc.presBoxKey = this.props.fieldKey;
             doc.collapsedHeight = hgt;
-            doc.height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)");
+            doc._height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)");
             const curScale = NumCast(doc.viewScale, null);
             if (curScale === undefined) {
                 doc.viewScale = 1;
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 0825580b7..f492ea773 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -9,6 +9,7 @@
     position: absolute;
     font-size: 10px;
     line-height: 1;
+    overflow: hidden;
 }
 .searchBox-bar {
     height: 32px;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d129fe33b..5f454e930 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -710,9 +710,9 @@ export namespace Doc {
     }
 
     let dt = 0;
-    export function linkFollowHighlight(destDoc: Doc) {
+    export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
         linkFollowUnhighlight();
-        Doc.HighlightDoc(destDoc);
+        Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
         document.addEventListener("pointerdown", linkFollowUnhighlight);
         const x = dt = Date.now();
@@ -726,10 +726,10 @@ export namespace Doc {
     export function IsHighlighted(doc: Doc) {
         return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
     }
-    export function HighlightDoc(doc: Doc) {
+    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
         runInAction(() => {
             highlightManager.HighlightedDoc.set(doc, true);
-            highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), true);
+            dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), true);
         });
     }
     export function UnHighlightDoc(doc: Doc) {
@@ -756,7 +756,7 @@ export namespace Doc {
             source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory :
             source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined;
     }
-    export function setChildDetailed(target: Doc, source?: Doc) {
+    export function setChildDetailedLayout(target: Doc, source?: Doc) {
         target.childDetailed = source && source.isTemplateDoc ? source : source &&
             source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory :
             source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined;
@@ -776,6 +776,7 @@ export namespace Doc {
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
 Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
 Scripting.addGlobal(function setChildLayout(target: any, source: any) { Doc.setChildLayout(target, source); });
+Scripting.addGlobal(function setChildDetailedLayout(target: any, source: any) { Doc.setChildDetailedLayout(target, source); });
 Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
 Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
 Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 6f99af8f6..35b266608 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -14,6 +14,7 @@ import { Utils } from "../../../Utils";
 import { nullAudio } from "../../../new_fields/URLField";
 import { DragManager } from "../../../client/util/DragManager";
 import { InkingControl } from "../../../client/views/InkingControl";
+import { CollectionViewType } from "../../../client/views/collections/CollectionView";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -54,7 +55,7 @@ export class CurrentUserUtils {
             { title: "cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 200, title: "an image of a cat" })' },
             { title: "record", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
             { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
-            { title: "presentation", icon: "tv", ignoreClick: true, drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { _width: 200, _height: 500, title: "a presentation trail" })' },
+            { title: "presentation", icon: "tv", ignoreClick: true, drag: `Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { _width: 200, _height: 500, _viewType: ${CollectionViewType.Stacking}, title: "a presentation trail" })` },
             { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { title: "mobile view", 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 },
@@ -246,7 +247,7 @@ export class CurrentUserUtils {
 
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
-        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", boxShadow: "0 0" });
+        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, boxShadow: "0 0" });
     }
 
     static setupMobileUploads(doc: Doc) {
-- 
cgit v1.2.3-70-g09d2

