From 659da12d9f3fe1d57a55dbb5a44ad30d03337aae Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 26 Jun 2020 16:40:24 -0400
Subject: updated pdfjs.  fixed position of blue dot for pdfs. enabled  fixed

---
 src/client/views/collections/CollectionSubView.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 00d6d59c8..570f9375e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -19,6 +19,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
 import { FieldViewProps } from "../nodes/FieldView";
 import React = require("react");
+import * as rp from 'request-promise';
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc | Doc[]) => boolean;
@@ -400,9 +401,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 const item = e.dataTransfer.items[i];
                 if (item.kind === "string" && item.type.includes("uri")) {
                     const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
-                    const type = "html";// (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+                    const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
                     if (type) {
-                        const doc = await DocUtils.DocumentFromType(type, stringContents, options);
+                        const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
                         doc && generatedDocuments.push(doc);
                     }
                 }
-- 
cgit v1.2.3-70-g09d2


From 1f41121904a27fbd77b6afa4ad1ffe3552aef24c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 28 Jun 2020 01:35:42 -0400
Subject: fixed zooming out on a collection when over a pdf.  fixed filters on
 nested collections.

---
 src/client/views/collections/CollectionSubView.tsx |  3 +--
 src/client/views/pdf/PDFViewer.tsx                 | 12 +++++++-----
 2 files changed, 8 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 570f9375e..3a13ac822 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -104,8 +104,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         }
         docFilters = () => {
             return this.props.ignoreFields?.includes("_docFilters") ? [] :
-                this.props.docFilters !== returnEmptyFilter ? this.props.docFilters() :
-                    Cast(this.props.Document._docFilters, listSpec("string"), []);
+                [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
         }
         @computed get childDocs() {
             const docFilters = this.docFilters();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d8130ce63..64f150dd5 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -641,11 +641,13 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        e.stopPropagation();
-        if (e.ctrlKey) {
-            const curScale = Number(this._pdfViewer.currentScaleValue);
-            this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
-            this._zoomed = Number(this._pdfViewer.currentScaleValue);
+        if (this.active()) {
+            e.stopPropagation();
+            if (e.ctrlKey) {
+                const curScale = Number(this._pdfViewer.currentScaleValue);
+                this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
+                this._zoomed = Number(this._pdfViewer.currentScaleValue);
+            }
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 29f9cedd937697c56ce3b400931b26c82a23721d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 29 Jun 2020 00:21:07 -0400
Subject: changed a lot of collection keys to start with "_" -- particularly
 for Stacking and Schema views.  Also added filtering to facet filters. 
 enabled facet filter for sidebar library.

---
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  1 -
 src/client/documents/Documents.ts                  | 40 +++++------
 src/client/util/CurrentUserUtils.ts                | 24 +++----
 .../util/Import & Export/DirectoryImportBox.tsx    |  2 +-
 src/client/views/MainView.tsx                      | 18 ++---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 10 +--
 .../views/collections/CollectionSchemaView.tsx     | 16 +++--
 .../views/collections/CollectionStackingView.tsx   | 77 +++++++++++-----------
 .../CollectionStackingViewFieldColumn.tsx          | 18 ++---
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 src/client/views/collections/CollectionView.tsx    |  8 +--
 .../views/collections/CollectionViewChromes.tsx    |  8 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 src/client/views/nodes/KeyValueBox.tsx             |  4 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |  4 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  4 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/fields/documentSchemas.ts                      | 10 ++-
 src/fields/util.ts                                 |  2 +-
 src/scraping/buxton/scraper.py                     |  3 +-
 22 files changed, 131 insertions(+), 131 deletions(-)

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

diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index a604c7de1..13bfb3a91 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -130,7 +130,6 @@ export namespace GooglePhotos {
             const uploads = await Transactions.WriteMediaItemsToServer(response);
             const children = uploads.map((upload: Transactions.UploadInformation) => {
                 const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
-                document.fillColumn = true;
                 document.contentSize = upload.contentSize;
                 return document;
             });
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f71984ca6..b70971c2d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -62,7 +62,7 @@ export interface DocumentOptions {
     _dimUnit?: string; // "px" or "*" (default = "*")
     _fitWidth?: boolean;
     _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
-    _LODdisable?: boolean;
+    _freeformLOD?: boolean; // whether to use LOD to render a freeform document
     _showTitleHover?: string; // 
     _showTitle?: string; // which field to display in the title area.  leave empty to have no title
     _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
@@ -99,7 +99,7 @@ export interface DocumentOptions {
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
     hideFilterView?: boolean; // whether to hide the filter popout on collections
-    hideHeadings?: boolean; // whether stacking view column headings should be hidden
+    _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
     targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
@@ -119,7 +119,7 @@ export interface DocumentOptions {
     defaultBackgroundColor?: string;
     isBackground?: boolean;
     isLinkButton?: boolean;
-    columnWidth?: number;
+    _columnWidth?: number;
     _fontSize?: number;
     _fontFamily?: string;
     curPage?: number;
@@ -137,7 +137,8 @@ export interface DocumentOptions {
     "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
     "onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
-    schemaColumns?: List<SchemaHeaderField>;
+    _columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
+    _schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
     dockingConfig?: string;
     annotationOn?: Doc;
     removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
@@ -428,8 +429,7 @@ export namespace Docs {
             const parent = TreeDocument([loading], {
                 title: "The Buxton Collection",
                 _width: 400,
-                _height: 400,
-                _LODdisable: true
+                _height: 400
             });
             const parentProto = Doc.GetProto(parent);
             const { _socket } = DocServer;
@@ -465,7 +465,7 @@ export namespace Docs {
                         return imageDoc;
                     });
                     // the main document we create
-                    const doc = StackingDocument(deviceImages, { title, _LODdisable: true, hero: new ImageField(constructed[0].url) });
+                    const doc = StackingDocument(deviceImages, { title, hero: new ImageField(constructed[0].url) });
                     doc.nameAliases = new List<string>([title.toLowerCase()]);
                     // add the parsed attributes to this main document
                     Doc.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } });
@@ -698,15 +698,15 @@ export namespace Docs {
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...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);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", ...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);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", ...options, _viewType: CollectionViewType.Linear }, id);
         }
 
         export function MapDocument(documents: Array<Doc>, options: DocumentOptions = {}) {
@@ -714,35 +714,35 @@ export namespace Docs {
         }
 
         export function CarouselDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Carousel });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Carousel });
         }
 
         export function Carousel3DDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Carousel3D });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Carousel3D });
         }
 
-        export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List(schemaColumns.length ? schemaColumns : [new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Schema });
+        export function SchemaDocument(schemaHeaders: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", _schemaHeaders: schemaHeaders.length ? new List(schemaHeaders) : undefined, ...options, _viewType: CollectionViewType.Schema });
         }
 
         export function TreeDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Tree }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Tree }, id);
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Stacking }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id);
         }
 
         export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multicolumn });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Multicolumn });
         }
         export function MultirowDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multirow });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Multirow });
         }
 
 
         export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Masonry });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Masonry });
         }
 
         export function LabelDocument(options?: DocumentOptions) {
@@ -1054,7 +1054,7 @@ export namespace DocUtils {
             });
         });
         if (x !== undefined && y !== undefined) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
+            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100 });
             newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
             newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
             newCollection._width = newCollection._height = 110;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2b0b2c738..d0ca0e57e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -43,7 +43,7 @@ export class CurrentUserUtils {
             const queryTemplate = Docs.Create.MulticolumnDocument(
                 [
                     Docs.Create.QueryDocument({ title: "query", _height: 200 }),
-                    Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true })
+                    Docs.Create.FreeformDocument([], { title: "data", _height: 100 })
                 ],
                 { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
             );
@@ -136,9 +136,9 @@ export class CurrentUserUtils {
         if (doc["template-button-switch"] === undefined) {
             const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
 
-            const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _LODdisable: true, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
+            const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
             const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1 });
-            const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, _LODdisable: true });
+            const no = FreeformDocument([], { title: "no", _height: 100, _width: 100 });
             const labelTemplate = {
                 doc: {
                     type: "doc", content: [{
@@ -193,10 +193,10 @@ export class CurrentUserUtils {
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
             const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: 12 };
-            const descriptionWrapperOpts = { title: "descriptions", _height: 300, columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
+            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
 
             const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
-            descriptionWrapper.sectionHeaders = new List<SchemaHeaderField>([
+            descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
                 new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false),
                 new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true),
                 new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true),
@@ -225,7 +225,7 @@ export class CurrentUserUtils {
         if (doc["template-buttons"] === undefined) {
             doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, {
                 title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title",
-                _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+                _autoHeight: true, _width: 500, _columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
         } else {
@@ -358,11 +358,11 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+                { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
         }
         if (doc.emptyCollection === undefined) {
             doc.emptyCollection = Docs.Create.FreeformDocument([],
-                { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+                { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform" });
         }
         if (doc.emptyDocHolder === undefined) {
             doc.emptyDocHolder = Docs.Create.DocumentDocument(
@@ -430,7 +430,7 @@ export class CurrentUserUtils {
         if (dragCreatorSet === undefined) {
             doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
                 title: "Basic Item Creators", _showTitle: "title", _xMargin: 0,
-                _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+                _autoHeight: true, _width: 500, _columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
         } else {
@@ -493,7 +493,7 @@ export class CurrentUserUtils {
 
     static setupMobileDoc(userDoc: Doc) {
         return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
-            columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
+            _columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
         });
     }
 
@@ -655,7 +655,7 @@ export class CurrentUserUtils {
         // Finally, setup the list of buttons to display in the sidebar
         if (doc["tabs-buttons"] === undefined) {
             doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
-                _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
+                _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode",
                 title: "sidebar btn row stack", backgroundColor: "dimGray",
             }));
             (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
@@ -704,7 +704,7 @@ export class CurrentUserUtils {
         if (doc.activePresentation === undefined) {
             doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
                 title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
-                _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
+                _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
             });
         }
     }
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index af6c57e68..77f13e9f4 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -161,7 +161,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
                 importContainer = Docs.Create.SchemaDocument(headers, docs, options);
             }
             runInAction(() => this.phase = 'External: uploading files to Google Photos...');
-            importContainer.singleColumn = false;
+            importContainer._columnsStack = false;
             await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 68b81ab4f..8f5a31b6c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -206,7 +206,6 @@ export class MainView extends React.Component {
             _width: this._panelWidth * .7,
             _height: this._panelHeight,
             title: "Collection " + workspaceCount,
-            _LODdisable: true
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
@@ -354,15 +353,6 @@ export class MainView extends React.Component {
         }
     }
 
-    @action
-    pointerOverDragger = () => {
-        // if (this.flyoutWidth === 0) {
-        //     this.flyoutWidth = 250;
-        //     this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
-        //     this._flyoutTranslate = false;
-        // }
-    }
-
     @action
     pointerLeaveDragger = () => {
         if (!this._flyoutTranslate) {
@@ -375,13 +365,13 @@ export class MainView extends React.Component {
     onPointerMove = (e: PointerEvent) => {
         this.flyoutWidth = Math.max(e.clientX, 0);
         Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false);
-        this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30;
+        this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30;
     }
     @action
     onPointerUp = (e: PointerEvent) => {
         if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) {
             this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
-            this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30);
+            this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30);
         }
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
@@ -470,7 +460,7 @@ export class MainView extends React.Component {
             }} >
                 <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
                     <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
-                        <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
+                        <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown}
                             style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
                             <span title="library View Dragger" style={{
                                 width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
@@ -497,7 +487,7 @@ export class MainView extends React.Component {
     public static expandFlyout = action(() => {
         MainView.Instance._flyoutTranslate = true;
         MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
-        MainView.Instance.sidebarButtonsDoc.columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
+        MainView.Instance.sidebarButtonsDoc._columnWidth = MainView.Instance.flyoutWidth / 3 - 30;
     });
 
     @computed get expandButton() {
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e0b53e762..627b22417 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -111,8 +111,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         const key = StrCast(this.props.parent.props.Document._pivotField);
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.parent.sectionHeaders) {
-                if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+            if (this.props.parent.columnHeaders) {
+                if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
                     return false;
                 }
             }
@@ -151,9 +151,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document._pivotField);
         this.props.docList.forEach(d => d[key] = undefined);
-        if (this.props.parent.sectionHeaders && this.props.headingObject) {
-            const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
-            this.props.parent.sectionHeaders.splice(index, 1);
+        if (this.props.parent.columnHeaders && this.props.headingObject) {
+            const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
+            this.props.parent.columnHeaders.splice(index, 1);
         }
     }));
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 56a2a517c..b780da97e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -240,10 +240,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
 
     @computed get columns() {
-        return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []);
+        return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
     }
     set columns(columns: SchemaHeaderField[]) {
-        this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns);
+        this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
     }
 
     @computed get childDocs() {
@@ -375,10 +375,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     constructor(props: SchemaTableProps) {
         super(props);
         // convert old schema columns (list of strings) into new schema columns (list of schema header fields)
-        const oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
-        if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") {
-            const newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
-            this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns);
+        const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
+        if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
+            const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
+            this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
+        } else if (this.props.Document._schemaHeaders === undefined) {
+            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
         }
     }
 
@@ -740,7 +742,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                 if(col === undefined) {
                     return (doc as any)[key][row + ${row}];
                 }
-                return (doc as any)[key][row + ${row}][(doc as any).schemaColumns[col + ${col}].heading];
+                return (doc as any)[key][row + ${row}][(doc as any)._schemaHeaders[col + ${col}].heading];
             }
             return ${script}`;
         const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4aab43b08..7bdf5e7df 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -43,27 +43,26 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     @observable _heightMap = new Map<string, number>();
     @observable _cursor: CursorProperty = "grab";
     @observable _scroll = 0; // used to force the document decoration to update when scrolling
-    @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
-    @computed get pivotField() { return StrCast(this.props.Document._pivotField); }
+    @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField)); }
+    @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
-    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
-    @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
-    @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
-    @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
+    @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
+    @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 0)); } // 2 * this.gridGap)); }
+    @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
+    @computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
-    @computed get showAddAGroup() { return (this.pivotField && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); }
+    @computed get showAddAGroup() { return (this.pivotField && (this.layoutDoc._chromeStatus !== 'view-mode' && this.layoutDoc._chromeStatus !== 'disabled')); }
     @computed get columnWidth() {
-        TraceMobx();
-        return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
-            this.isStackingView ? Number.MAX_VALUE : this.props.Document.columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.props.Document.columnWidth, 250));
+        return Math.min(this.props.PanelWidth() / this.props.ContentScaling() - 2 * this.xMargin,
+            this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
     }
     @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
 
     constructor(props: any) {
         super(props);
 
-        if (this.sectionHeaders === undefined) {
-            this.props.Document.sectionHeaders = new List<SchemaHeaderField>();
+        if (this.columnHeaders === undefined) {
+            this.layoutDoc._columnHeaders = new List<SchemaHeaderField>();
         }
     }
 
@@ -89,14 +88,14 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     }
 
     get Sections() {
-        if (!this.pivotField || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+        if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
-        if (this.sectionHeaders === undefined) {
-            setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
+        if (this.columnHeaders === undefined) {
+            setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
-        const sectionHeaders = Array.from(this.sectionHeaders);
-        const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        const columnHeaders = Array.from(this.columnHeaders);
+        const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
         let changed = false;
         this.filteredChildren.map(d => {
             const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
@@ -105,26 +104,26 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
             // look for if header exists already
-            const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
+            const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
             }
             else {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
-                sectionHeaders.push(newSchemaHeader);
+                columnHeaders.push(newSchemaHeader);
                 changed = true;
             }
         });
         // remove all empty columns if hideHeadings is set
-        if (this.props.Document.hideHeadings) {
+        if (this.layoutDoc._columnsHideIfEmpty) {
             Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
                 fields.delete(header);
-                sectionHeaders.splice(sectionHeaders.indexOf(header), 1);
+                columnHeaders.splice(columnHeaders.indexOf(header), 1);
                 changed = true;
             });
         }
-        changed && setTimeout(action(() => { if (this.sectionHeaders) { this.sectionHeaders.length = 0; this.sectionHeaders.push(...sectionHeaders); } }), 0);
+        changed && setTimeout(action(() => { if (this.columnHeaders) { this.columnHeaders.length = 0; this.columnHeaders.push(...columnHeaders); } }), 0);
         return fields;
     }
 
@@ -136,7 +135,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
         if (!layoutDoc._fitWidth && nw && nh) {
             const aspect = nw && nh ? nh / nw : 1;
-            if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+            if (!(this.layoutDoc._columnsFill)) wid = Math.min(layoutDoc[WidthSym](), wid);
             return wid * aspect;
         }
         return layoutDoc._fitWidth ? wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1) : layoutDoc[HeightSym]();
@@ -147,7 +146,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
             () => this.pivotField,
-            () => this.props.Document.sectionHeaders = new List()
+            () => this.layoutDoc._columnHeaders = new List()
         );
     }
     componentWillUnmount() {
@@ -211,7 +210,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             fitToBox={false}
             dontRegisterView={this.props.dontRegisterView}
             rootSelected={this.rootSelected}
-            dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+            dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
@@ -236,7 +235,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         if (!d) return 0;
         const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
-        return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+        return Math.min(nw && !this.layoutDoc._columnsFill ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
@@ -246,7 +245,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
         if (!layoutDoc._fitWidth && nw && nh) {
             const aspect = nw && nh ? nh / nw : 1;
-            if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+            if (!(this.layoutDoc._columnsFill)) wid = Math.min(layoutDoc[WidthSym](), wid);
             return wid * aspect;
         }
         return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
@@ -259,7 +258,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     }
     @action
     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
-        this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta[0]);
+        this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
         return false;
     }
 
@@ -341,7 +340,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                         }
                     }));
@@ -388,7 +387,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
                         }
                     }));
@@ -411,9 +410,9 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
 
     @action
     addGroup = (value: string) => {
-        if (value && this.sectionHeaders) {
+        if (value && this.columnHeaders) {
             const schemaHdrField = new SchemaHeaderField(value);
-            this.sectionHeaders.push(schemaHdrField);
+            this.columnHeaders.push(schemaHdrField);
             DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
             return true;
         }
@@ -421,22 +420,22 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     }
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
-        const descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
+        const descending = StrCast(this.layoutDoc._columnsSort) === "descending";
         const firstEntry = descending ? b : a;
         const secondEntry = descending ? a : b;
         return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
     }
 
     onToggle = (checked: Boolean) => {
-        this.props.Document._chromeStatus = checked ? "collapsed" : "view-mode";
+        this.layoutDoc._chromeStatus = checked ? "collapsed" : "view-mode";
     }
 
     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()) {
             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" });
-            subItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+            subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
+            subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
         }
     }
@@ -446,7 +445,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
         if (this.pivotField) {
             const entries = Array.from(this.Sections.entries());
-            sections = entries.sort(this.sortFunc);
+            sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
         }
         return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
     }
@@ -489,10 +488,10 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                             style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
                             <EditableView {...editableViewProps} />
                         </div>}
-                    {this.props.Document._chromeStatus !== 'disabled' && this.props.isSelected() ? <Switch
+                    {this.layoutDoc._chromeStatus !== 'disabled' && this.props.isSelected() ? <Switch
                         onChange={this.onToggle}
                         onClick={this.onToggle}
-                        defaultChecked={this.props.Document._chromeStatus !== 'view-mode'}
+                        defaultChecked={this.layoutDoc._chromeStatus !== 'view-mode'}
                         checkedChildren="edit"
                         unCheckedChildren="view"
                     /> : null}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b60ed853b..b147b089b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -96,8 +96,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         const key = StrCast(this.props.parent.props.Document._pivotField);
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.parent.sectionHeaders) {
-                if (this.props.parent.sectionHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+            if (this.props.parent.columnHeaders) {
+                if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
                     return false;
                 }
             }
@@ -148,9 +148,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     deleteColumn = () => {
         const key = StrCast(this.props.parent.props.Document._pivotField);
         this.props.docList.forEach(d => d[key] = undefined);
-        if (this.props.parent.sectionHeaders && this.props.headingObject) {
-            const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
-            this.props.parent.sectionHeaders.splice(index, 1);
+        if (this.props.parent.columnHeaders && this.props.headingObject) {
+            const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
+            this.props.parent.columnHeaders.splice(index, 1);
         }
     }
 
@@ -168,7 +168,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
         const alias = Doc.MakeAlias(this.props.parent.props.Document);
-        alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.props.Document.sectionHeaders, listSpec(SchemaHeaderField))?.length || 1);
+        alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.columnHeaders, listSpec(SchemaHeaderField))?.length || 1);
         alias._pivotField = undefined;
         const key = StrCast(this.props.parent.props.Document._pivotField);
         let value = this.getValue(this._heading);
@@ -259,8 +259,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     }
                 }, 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: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
+        layoutItems.push({ description: ":freeform", event: () => this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
+        layoutItems.push({ description: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200 })), 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" });
 
@@ -359,7 +359,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     background: this._background
                 }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
-                {this.props.parent.Document.hideHeadings ? (null) : headingView}
+                {this.props.parent.Document._columnsHideIfEmpty ? (null) : headingView}
                 {
                     this.collapsed ? (null) :
                         <div>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3a13ac822..49480e759 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -341,7 +341,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                                     const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect();
                                     const x = (rect?.x || 0);
                                     const y = NumCast(srcWeb._scrollTop) + (rect?.y || 0);
-                                    const anchor = Docs.Create.FreeformDocument([], { _LODdisable: true, _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
+                                    const anchor = Docs.Create.FreeformDocument([], { _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
                                     anchor.context = srcWeb;
                                     const key = Doc.LayoutFieldKey(srcWeb);
                                     Doc.AddDocToList(srcWeb, key + "-annotations", anchor);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d54f4d6e6..f6f6fb7cc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -176,7 +176,7 @@ class TreeView extends React.Component<TreeViewProps> {
         })}
         OnFillDown={undoBatch((value: string) => {
             Doc.SetInPlace(this.doc, key, value, false);
-            const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List<string>([Templates.Title.Layout]) });
+            const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
             Doc.SetInPlace(this.doc, "editTitle", undefined, false);
             Doc.SetInPlace(doc, "editTitle", "*", false);
             return this.props.addDocument(doc);
@@ -828,7 +828,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                         SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
                         OnFillDown={undoBatch((value: string) => {
                             Doc.SetInPlace(this.dataDoc, "title", value, false);
-                            const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List<string>([Templates.Title.Layout]) });
+                            const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
                             Doc.SetInPlace(doc, "editTitle", "*", false);
                             this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
                         })} />}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5c87bc483..f2ffe7835 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -207,8 +207,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             case CollectionViewType.Pile: { return (<CollectionPileView key="collview" {...props} />); }
             case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
             case CollectionViewType.Carousel3D: { return (<CollectionCarousel3DView 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} />); }
+            case CollectionViewType.Stacking: { this.props.Document._columnsStack = true; return (<CollectionStackingView key="collview" {...props} />); }
+            case CollectionViewType.Masonry: { this.props.Document._columnsStack = false; return (<CollectionStackingView key="collview" {...props} />); }
             case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
             case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
             case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
@@ -360,7 +360,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         const facets = new Set<string>();
         this.childDocs.filter(child => child).forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
         Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
-        return Array.from(facets);
+        return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
     }
 
     /**
@@ -534,7 +534,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
                     :
                     ""))}
-            {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
+            {(!this.props.isSelected() || this.props.Document.hideFilterView) && !this.props.Document.forceActive ? (null) :
                 <div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
                     style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "55%" }} />
             }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 276bccede..ab0df88f8 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -406,7 +406,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
     @observable private _currentKey: string = "";
     @observable private suggestions: string[] = [];
 
-    @computed private get descending() { return BoolCast(this.props.CollectionView.props.Document.stackingHeadersSortDescending); }
+    @computed private get descending() { return StrCast(this.props.CollectionView.props.Document._columnsSort) === "descending"; }
     @computed get pivotField() { return StrCast(this.props.CollectionView.props.Document._pivotField); }
 
     getKeySuggestions = async (value: string): Promise<string[]> => {
@@ -450,7 +450,11 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
         return true;
     }
 
-    @action toggleSort = () => { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; };
+    @action toggleSort = () => {
+        this.props.CollectionView.props.Document._columnsSort =
+        this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" :
+            this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending";
+    };
     @action resetValue = () => { this._currentKey = this.pivotField; };
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5135c4ae4..ba20e9830 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1226,7 +1226,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
         if (!Doc.UserDoc().noviceMode) {
             optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
-            optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+            optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
             optionItems.push({
                 description: "Import document", icon: "upload", event: ({ x, y }) => {
                     const input = document.createElement("input");
@@ -1390,7 +1390,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
                 height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
             }}>
-            {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
+            {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
                 this.placeholder : this.marqueeView}
             <CollectionFreeFormOverlayView elements={this.elementFunc} />
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 099859109..97ed74c10 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -352,7 +352,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined,
             _width: bounds.width,
             _height: bounds.height,
-            _LODdisable: true,
             title: "a nested collection",
         });
         selected.forEach(d => d.context = newCollection);
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index d375466c9..b732f5f83 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -169,8 +169,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     getTemplate = async () => {
         const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template" });
-        parent.singleColumn = false;
-        parent.columnWidth = 100;
+        parent._columnsStack = false;
+        parent._columnWidth = 100;
         for (const row of this.rows.filter(row => row.isChecked)) {
             await this.createTemplateField(parent, row);
             row.uncheck();
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8c16f4a1a..8718bf329 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -184,9 +184,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         if (container) {
             const alias = Doc.MakeAlias(container.props.Document);
             alias.viewType = CollectionViewType.Time;
-            let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField));
+            let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
             if (!list) {
-                alias.schemaColumns = list = new List<SchemaHeaderField>();
+                alias._columnHeaders = list = new List<SchemaHeaderField>();
             }
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
             list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index ba3230801..6a85e3b7c 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -275,7 +275,7 @@ export class RichTextRules {
                     if (!fieldKey) {
                         if (docid) {
                             DocServer.GetRefField(docid).then(docx => {
-                                const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true, }, docid);
+                                const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, }, docid);
                                 DocUtils.Publish(target, docid, returnFalse, returnFalse);
                                 DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
                             });
@@ -305,7 +305,7 @@ export class RichTextRules {
                     if (!fieldKey && !docid) return state.tr;
                     docid && DocServer.GetRefField(docid).then(docx => {
                         if (!(docx instanceof Doc && docx)) {
-                            const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true }, docid);
+                            const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
                             DocUtils.Publish(docx, docid, returnFalse, returnFalse);
                         }
                     });
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5ed1e6239..372d01b9c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -293,7 +293,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         let minY = Number.MAX_VALUE;
         if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
             const anno = this._savedAnnotations.values()[0][0];
-            const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, _LODdisable: true, title: "Annotation on " + this.Document.title });
+            const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
             if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
             if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
             if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 40dadf5a8..97f62c9d4 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -2,6 +2,8 @@ import { makeInterface, createSchema, listSpec } from "./Schema";
 import { ScriptField } from "./ScriptField";
 import { Doc } from "./Doc";
 import { DateField } from "./DateField";
+import { SchemaHeaderField } from "./SchemaHeaderField";
+import { Schema } from "prosemirror-model";
 
 export const documentSchema = createSchema({
     // content properties
@@ -43,10 +45,16 @@ export const documentSchema = createSchema({
     _showTitleHover: "string",  // the showTitle should be shown only on hover
     _showAudio: "boolean",      // whether to show the audio record icon on documents
     _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
-    _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
+    _freeformLOD: "boolean",    // whether to enable LOD switching for CollectionFreeFormViews
     _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
     _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
     _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
+    _columnsFill: "boolean",    // whether documents in a stacking view column should be sized to fill the column
+    _columnsSort: "string",     // how a document should be sorted "ascending", "descending", undefined (none)   
+    _columnsStack: "boolean",   // whether a stacking document stacks vertically (as opposed to masonry horizontal)
+    _columnsHideIfEmpty: "boolean",   // whether empty stacking view column headings should be hidden
+    _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
+    _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
     _fontSize: "number",
     _fontFamily: "string",
     _sidebarWidthPercent: "string", // percent of text window width taken up by sidebar
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c4affb2d7..2869ebed1 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -108,7 +108,7 @@ export function OVERRIDE_ACL(val: boolean) {
 }
 
 const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
-    "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
+    "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
     if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true;
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index ed122e544..312915e2d 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -91,14 +91,13 @@ def write_collection(parse_results, display_fields, storage_key, viewType):
             "zIndex": 2,
             "libraryBrush": False,
             "_viewType": viewType,
-            "_LODdisable": True
         },
         "__type": "Doc"
     }
 
     fields["proto"] = protofy(common_proto_id)
     fields[storage_key] = listify(proxify_guids(view_guids))
-    fields["schemaColumns"] = listify(display_fields)
+    fields["_columnHeaders"] = listify(display_fields)
     fields["author"] = "Bill Buxton"
     fields["creationDate"] = {
         "date": datetime.datetime.utcnow().microsecond,
-- 
cgit v1.2.3-70-g09d2


From cb8b5b3d9e735c88b9efdd9b4a444cac40fdedb2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 29 Jun 2020 10:55:27 -0400
Subject: cleaned up undo handling on internal drag/drop

---
 src/client/util/CurrentUserUtils.ts                                   | 2 +-
 src/client/util/DragManager.ts                                        | 4 ++--
 src/client/views/collections/CollectionSubView.tsx                    | 1 -
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 1 -
 4 files changed, 3 insertions(+), 5 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d0ca0e57e..9c1881ef8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -654,7 +654,7 @@ export class CurrentUserUtils {
 
         // Finally, setup the list of buttons to display in the sidebar
         if (doc["tabs-buttons"] === undefined) {
-            doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
+            doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], {
                 _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode",
                 title: "sidebar btn row stack", backgroundColor: "dimGray",
             }));
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0db3963b2..2ceafff30 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -202,7 +202,6 @@ export namespace DragManager {
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc);
             return dropDoc;
         };
-        const batch = UndoManager.StartBatch("dragging");
         const finishDrag = (e: DragCompleteEvent) => {
             const docDragData = e.docDragData;
             if (docDragData && !docDragData.droppedDocuments.length) {
@@ -216,7 +215,6 @@ export namespace DragManager {
                     const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
                     remProps.map(prop => drop[prop] = undefined);
                 });
-                batch.end();
             }
             return e;
         };
@@ -315,6 +313,7 @@ export namespace DragManager {
     export let docsBeingDragged: Doc[] = [];
     export let CanEmbed = false;
     export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
+        const batch = UndoManager.StartBatch("dragging");
         eles = eles.filter(e => e);
         CanEmbed = false;
         if (!dragDiv) {
@@ -449,6 +448,7 @@ export namespace DragManager {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
             SnappingManager.clearSnapLines();
+            batch.end();
         });
 
         AbortDrag = () => {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 49480e759..29144abaf 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -208,7 +208,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
 
         addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc);
 
-        @undoBatch
         @action
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
             const docDragData = de.complete.docDragData;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ba20e9830..546a4307c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -194,7 +194,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
     }
 
-    @undoBatch
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
         if (!super.onInternalDrop(e, de)) return false;
-- 
cgit v1.2.3-70-g09d2


From 6a75dac53b9fab5b96f2cce4937b59f1e29928f9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 30 Jun 2020 01:08:45 -0400
Subject: added a docFilter for a substring match.  fixed formattedTextBox to
 not keep adding user_mark storedMarks on every load

---
 package-lock.json                                  | 44 ++++++----------------
 src/client/util/SearchUtil.ts                      |  2 +-
 src/client/views/collections/CollectionSubView.tsx | 13 +++++--
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 src/client/views/collections/CollectionView.tsx    | 26 +++++++++----
 .../views/nodes/formattedText/FormattedTextBox.tsx |  9 +++--
 src/client/views/nodes/formattedText/marks_rts.ts  |  1 +
 src/fields/Doc.ts                                  | 33 +++++++++-------
 8 files changed, 69 insertions(+), 63 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 53b2fc009..957641213 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2955,8 +2955,7 @@
                         },
                         "ansi-regex": {
                             "version": "2.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "aproba": {
                             "version": "1.2.0",
@@ -2974,13 +2973,11 @@
                         },
                         "balanced-match": {
                             "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "brace-expansion": {
                             "version": "1.1.11",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "balanced-match": "^1.0.0",
                                 "concat-map": "0.0.1"
@@ -2993,18 +2990,15 @@
                         },
                         "code-point-at": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "concat-map": {
                             "version": "0.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "console-control-strings": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "core-util-is": {
                             "version": "1.0.2",
@@ -3107,8 +3101,7 @@
                         },
                         "inherits": {
                             "version": "2.0.4",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "ini": {
                             "version": "1.3.5",
@@ -3118,7 +3111,6 @@
                         "is-fullwidth-code-point": {
                             "version": "1.0.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "number-is-nan": "^1.0.0"
                             }
@@ -3131,20 +3123,17 @@
                         "minimatch": {
                             "version": "3.0.4",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "brace-expansion": "^1.1.7"
                             }
                         },
                         "minimist": {
                             "version": "1.2.5",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -3161,7 +3150,6 @@
                         "mkdirp": {
                             "version": "0.5.3",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "minimist": "^1.2.5"
                             }
@@ -3217,8 +3205,7 @@
                         },
                         "npm-normalize-package-bin": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "npm-packlist": {
                             "version": "1.4.8",
@@ -3243,8 +3230,7 @@
                         },
                         "number-is-nan": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "object-assign": {
                             "version": "4.1.1",
@@ -3254,7 +3240,6 @@
                         "once": {
                             "version": "1.4.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "wrappy": "1"
                             }
@@ -3323,8 +3308,7 @@
                         },
                         "safe-buffer": {
                             "version": "5.1.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "safer-buffer": {
                             "version": "2.1.2",
@@ -3354,7 +3338,6 @@
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -3372,7 +3355,6 @@
                         "strip-ansi": {
                             "version": "3.0.1",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "ansi-regex": "^2.0.0"
                             }
@@ -3411,13 +3393,11 @@
                         },
                         "wrappy": {
                             "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         }
                     }
                 }
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 5679c0a14..1ac68480e 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -74,7 +74,7 @@ export namespace SearchUtil {
         const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
         for (let i = 0; i < ids.length; i++) {
             const testDoc = docs[i];
-            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
+            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
                 theDocs.push(testDoc);
                 theLines.push([]);
             }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 29144abaf..b76859d90 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -7,7 +7,7 @@ import { List } from "../../../fields/List";
 import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
 import { WebField } from "../../../fields/URLField";
-import { Cast, ScriptCast, NumCast } from "../../../fields/Types";
+import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
 import { Upload } from "../../../server/SharedMediaTypes";
 import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
@@ -136,8 +136,12 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const filteredDocs = docFilters.length && !this.props.dontRegisterView ? childDocs.filter(d => {
                 for (const facetKey of Object.keys(filterFacets)) {
                     const facet = filterFacets[facetKey];
-                    const satisfiesFacet = Object.keys(facet).some(value =>
-                        (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value));
+                    const satisfiesFacet = Object.keys(facet).some(value => {
+                        if (facet[value] === "match") {
+                            return d[facetKey] === undefined || Cast(d[facetKey], RichTextField)?.Text.includes(value);
+                        }
+                        return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value);
+                    });
                     if (!satisfiesFacet) {
                         return false;
                     }
@@ -471,4 +475,5 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
 import { CollectionView } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
+import { SelectionManager } from "../../util/SelectionManager"; import { RichTextField } from "../../../fields/RichTextField";
+
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f6f6fb7cc..c1569a172 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -386,8 +386,8 @@ class TreeView extends React.Component<TreeViewProps> {
         e.stopPropagation();
     }
 
-    @computed
-    get renderBullet() {
+    @computed get renderBullet() {
+        TraceMobx();
         const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
         return <div className="bullet"
             title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f2ffe7835..c7b1afbd6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -47,6 +47,7 @@ import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import './CollectionView.scss';
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 import { UndoManager } from '../../util/UndoManager';
+import { RichTextField } from '../../../fields/RichTextField';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -357,8 +358,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
     }
     @computed get _allFacets() {
+        TraceMobx();
         const facets = new Set<string>();
-        this.childDocs.filter(child => child).forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
+        this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
         Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
         return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
     }
@@ -387,8 +389,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             }
         } else {
             const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
-            const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
-                set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
+            var rtfields = 0;
+            const facetValues = Array.from(allCollectionDocs.reduce((set, child) => {
+                const field = child[facetHeader] as Field;
+                if (field instanceof RichTextField) rtfields++;
+                return set.add(Field.toString(field));
+            }, new Set<string>()));
 
             let nonNumbers = 0;
             let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
@@ -402,13 +408,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 }
             });
             let newFacet: Opt<Doc>;
-            if (nonNumbers / allCollectionDocs.length < .1) {
-                newFacet = Docs.Create.SliderDocument({ title: facetHeader });
+            if (rtfields / allCollectionDocs.length > 0.1) {
+                newFacet = Docs.Create.TextDocument("", { _width: 200, _height: 30, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
+                Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+                newFacet.target = this.props.Document;
+                const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`;
+                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
+            } else if (nonNumbers / allCollectionDocs.length < .1) {
+                newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
                 const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                newFacet.treeViewExpandedView = "layout";
-                newFacet.treeViewOpen = true;
                 const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
                 const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
                 newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
@@ -418,7 +428,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 newFacet.target = this.props.Document;
                 const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
                 newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
-
                 Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
             } else {
                 newFacet = new Doc();
@@ -445,6 +454,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
     }
     @computed get filterView() {
+        TraceMobx();
         const facetCollection = this.props.Document;
         const flyout = (
             <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90f379525..222a359d1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -22,7 +22,7 @@ import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { createSchema, makeInterface } from "../../../../fields/Schema";
-import { Cast, DateCast, NumCast, StrCast } from "../../../../fields/Types";
+import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types";
 import { TraceMobx, OVERRIDE_ACL } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
@@ -203,13 +203,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!this.dataDoc[AclSym]) {
                 if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
                     this._applyingChange = true;
-                    this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+                    (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
                     if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                         if (json !== curLayout?.Data) {
                             !curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize));
                             !curText && tx.storedMarks?.map(m => m.type.name === "pFontFamily" && (Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily));
                             this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
                             this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                            ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                         }
                     } else { // if we've deleted all the text in a note driven by a template, then restore the template data
                         this.dataDoc[this.props.fieldKey] = undefined;
@@ -970,7 +971,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
         (selectOnLoad /* || !rtfField?.Text*/) && this._editorView!.focus();
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
-        this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
+        if (!this._editorView!.state.storedMarks || !this._editorView!.state.storedMarks.some(mark => mark.type === schema.marks.user_mark)) {
+            this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
+        }
     }
     getFont(font: string) {
         switch (font) {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index b09ac0678..54b61aa20 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -270,6 +270,7 @@ export const marks: { [index: string]: MarkSpec } = {
             userid: { default: "" },
             modified: { default: "when?" }, // 1 second intervals since 1970
         },
+        excludes: "user_mark",
         group: "inline",
         toDOM(node: any) {
             const uid = node.attrs.userid.replace(".", "").replace("@", "");
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index bef8acb06..e4d11dd4d 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -945,20 +945,27 @@ export namespace Doc {
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded 
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) {
+    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) {
         const docFilters = Cast(container._docFilters, listSpec("string"), []);
-        for (let i = 0; i < docFilters.length; i += 3) {
-            if (docFilters[i] === key && docFilters[i + 1] === value) {
-                docFilters.splice(i, 3);
-                break;
+        runInAction(() => {
+            for (let i = 0; i < docFilters.length; i += 3) {
+                if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) {
+                    if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return;
+                    docFilters.splice(i, 3);
+                    break;
+                }
             }
-        }
-        if (typeof modifiers === "string") {
-            docFilters.push(key);
-            docFilters.push(value);
-            docFilters.push(modifiers);
-            container._docFilters = new List<string>(docFilters);
-        }
+            if (typeof modifiers === "string") {
+                if (!docFilters.length && modifiers === "match" && value === undefined) {
+                    container._docFilters = undefined;
+                } else {
+                    docFilters.push(key);
+                    docFilters.push(value);
+                    docFilters.push(modifiers);
+                    container._docFilters = new List<string>(docFilters);
+                }
+            }
+        })
     }
     export function readDocRangeFilter(doc: Doc, key: string) {
         const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []);
@@ -1168,5 +1175,5 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
             (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
     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 setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "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
-- 
cgit v1.2.3-70-g09d2


From eb8399d057089a7b90d756fc69df096becad9a7f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 30 Jun 2020 02:21:31 -0400
Subject: added heuristic to filters to identify text fields based on being RTF
 or having multiple words. fixed text box padding when used as filter input. 
 fixed selection of items in sidebar when RichTextMenu is pinned.

---
 src/client/views/MainView.tsx                             |  5 +++--
 src/client/views/collections/CollectionSubView.tsx        |  6 +++---
 src/client/views/collections/CollectionTreeView.tsx       |  6 +++---
 src/client/views/collections/CollectionView.tsx           | 15 +++++++++------
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 5 files changed, 19 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cea664543..200486279 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -383,7 +383,8 @@ export class MainView extends React.Component {
             doc.dockingConfig ? this.openWorkspace(doc) :
                 CollectionDockingView.AddRightSplit(doc, libraryPath);
     }
-    mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
+    sidebarScreenToLocal = () => new Transform(0, RichTextMenu.Instance.Pinned ? -35 : 0, 1);
+    mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight);
 
     @computed get flyout() {
         const sidebarContent = this.userDoc?.["tabs-panelContainer"];
@@ -402,7 +403,7 @@ export class MainView extends React.Component {
                     pinToPres={emptyFunction}
                     removeDocument={undefined}
                     onClick={undefined}
-                    ScreenToLocalTransform={Transform.Identity}
+                    ScreenToLocalTransform={this.sidebarScreenToLocal}
                     ContentScaling={returnOne}
                     NativeHeight={returnZero}
                     NativeWidth={returnZero}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b76859d90..f39c1ae28 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { basename } from 'path';
 import CursorField from "../../../fields/CursorField";
-import { Doc, Opt } from "../../../fields/Doc";
+import { Doc, Opt, Field } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
 import { listSpec } from "../../../fields/Schema";
@@ -138,7 +138,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     const facet = filterFacets[facetKey];
                     const satisfiesFacet = Object.keys(facet).some(value => {
                         if (facet[value] === "match") {
-                            return d[facetKey] === undefined || Cast(d[facetKey], RichTextField)?.Text.includes(value);
+                            return d[facetKey] === undefined || Field.toString(d[facetKey] as Field).includes(value);
                         }
                         return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value);
                     });
@@ -475,5 +475,5 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
 import { CollectionView } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager"; import { RichTextField } from "../../../fields/RichTextField";
+import { SelectionManager } from "../../util/SelectionManager";
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c1569a172..620b977fa 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -304,7 +304,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
-    rtfHeight = () => this.rtfWidth() < this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
 
     @computed get renderContent() {
         TraceMobx();
@@ -332,8 +332,8 @@ class TreeView extends React.Component<TreeViewProps> {
             </div></ul>;
         } else {
             const layoutDoc = this.layoutDoc;
-            const panelHeight = layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight;
-            const panelWidth = layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth;
+            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
+            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
             return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
                 <ContentFittingDocumentView
                     Document={layoutDoc}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c7b1afbd6..6acf78af7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -48,6 +48,7 @@ import './CollectionView.scss';
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 import { UndoManager } from '../../util/UndoManager';
 import { RichTextField } from '../../../fields/RichTextField';
+import { TextField } from '../../util/ProsemirrorCopy/prompt';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -359,7 +360,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     }
     @computed get _allFacets() {
         TraceMobx();
-        const facets = new Set<string>();
+        const facets = new Set<string>(["type", "text", "data", "author", "ACL"]);
         this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
         Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
         return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
@@ -392,8 +393,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             var rtfields = 0;
             const facetValues = Array.from(allCollectionDocs.reduce((set, child) => {
                 const field = child[facetHeader] as Field;
-                if (field instanceof RichTextField) rtfields++;
-                return set.add(Field.toString(field));
+                const fieldStr = Field.toString(field);
+                if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++;
+                return set.add(fieldStr);
             }, new Set<string>()));
 
             let nonNumbers = 0;
@@ -408,13 +410,14 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 }
             });
             let newFacet: Opt<Doc>;
-            if (rtfields / allCollectionDocs.length > 0.1) {
-                newFacet = Docs.Create.TextDocument("", { _width: 200, _height: 30, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
+            if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) {
+                newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet.target = this.props.Document;
+                newFacet._textBoxPadding = 4;
                 const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`;
                 newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
-            } else if (nonNumbers / allCollectionDocs.length < .1) {
+            } else if (nonNumbers / facetValues.length < .1) {
                 newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
                 const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 222a359d1..2cb55e0fa 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1308,7 +1308,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         onScroll={this.onscrolled} onDrop={this.ondrop} >
                         <div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
                             style={{
-                                padding: `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
+                                padding: this.layoutDoc._textBoxPadding ? this.layoutDoc._textBoxPadding : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
                                 pointerEvents: !this.props.isSelected() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : "all") : undefined
                             }}
                         />
-- 
cgit v1.2.3-70-g09d2


From 623a1804a16ea625d373ac413194a780c5c1f17e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 30 Jun 2020 09:46:49 -0400
Subject: trying to get Youtube player to work better.

---
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 src/client/views/nodes/VideoBox.tsx                | 20 ++++++++++----------
 2 files changed, 11 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f39c1ae28..d107db86b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -358,7 +358,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
 
             if (text) {
-                if (text.includes("www.youtube.com/watch")) {
+                if (text.includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
                     const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0];
                     addDocument(Docs.Create.VideoDocument(url, {
                         ...options,
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 71556bfd3..967d94fa6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -58,21 +58,21 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
 
     @action public Play = (update: boolean = true) => {
         this._playing = true;
-        update && this.player && this.player.play();
-        update && this._youtubePlayer && this._youtubePlayer.playVideo();
+        update && this.player?.play();
+        update && this._youtubePlayer?.playVideo();
         this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
         this.updateTimecode();
     }
 
     @action public Seek(time: number) {
-        this._youtubePlayer && this._youtubePlayer.seekTo(Math.round(time), true);
+        this._youtubePlayer?.seekTo(Math.round(time), true);
         this.player && (this.player.currentTime = time);
     }
 
     @action public Pause = (update: boolean = true) => {
         this._playing = false;
-        update && this.player && this.player.pause();
-        update && this._youtubePlayer && this._youtubePlayer.pauseVideo && this._youtubePlayer.pauseVideo();
+        update && this.player?.pause();
+        update && this._youtubePlayer?.pauseVideo();
         this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
         this._playTimer = undefined;
         this.updateTimecode();
@@ -261,16 +261,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
             if (started && event.data === YT.PlayerState.PLAYING) {
                 started = false;
-                this._youtubePlayer && this._youtubePlayer.unMute();
-                this.Pause();
+                this._youtubePlayer?.unMute();
+                //this.Pause();
                 return;
             }
             if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
             if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
         });
         const onYoutubePlayerReady = (event: any) => {
-            this._reactionDisposer && this._reactionDisposer();
-            this._youtubeReactionDisposer && this._youtubeReactionDisposer();
+            this._reactionDisposer?.();
+            this._youtubeReactionDisposer?.();
             this._reactionDisposer = reaction(() => this.layoutDoc.currentTimecode, () => !this._playing && this.Seek((this.layoutDoc.currentTimecode || 0)));
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, Doc.GetSelectedTool()], () => {
                 const interactive = Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
@@ -346,7 +346,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         const start = untracked(() => Math.round((this.layoutDoc.currentTimecode || 0)));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
     }
 
     @action.bound
-- 
cgit v1.2.3-70-g09d2


From 973e49dc36f208cc7f9a261cbe03e8d65d001b92 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 Jul 2020 01:53:42 -0400
Subject: added isPushpin.  added 'hidden' for DocumentViews to stop displaying
 them.  made pushpin's FontIconBox's.  made Esc close context menu.

---
 src/client/util/DocumentManager.ts                 |  8 +-
 src/client/views/ContextMenu.tsx                   |  2 +
 src/client/views/DocumentDecorations.tsx           | 92 +++++++++++-----------
 src/client/views/GlobalKeyHandler.ts               |  6 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 src/client/views/collections/CollectionView.tsx    | 12 +--
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/FontIconBox.tsx             |  9 ++-
 8 files changed, 74 insertions(+), 60 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb5d1717e..1fa5faeb3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -146,7 +146,7 @@ export class DocumentManager {
         };
         const docView = getFirstDocView(targetDoc, originatingDoc);
         let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
-        if (annotatedDoc) {
+        if (annotatedDoc && !linkDoc?.isPushpin) {
             const first = getFirstDocView(annotatedDoc);
             if (first) {
                 annotatedDoc = first.props.Document;
@@ -156,7 +156,11 @@ export class DocumentManager {
             }
         }
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
-            docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
+            if (linkDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden;
+            else {
+                docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
+                docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
+            }
             highlight();
         } else {
             const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 941d7b44a..07f7b8e6d 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -155,9 +155,11 @@ export class ContextMenu extends React.Component {
 
     @action
     closeMenu = () => {
+        const wasOpen = this._display;
         this.clearItems();
         this._display = false;
         this._shouldDisplay = false;
+        return wasOpen;
     }
 
     @computed get filteredItems(): (OriginalMenuProps | string[])[] {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4fda10926..a45ef8862 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -600,52 +600,54 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
             }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
-            <div className="documentDecorations-container" ref={this.setTextBar} style={{
-                width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
-                left: bounds.x - this._resizeBorderWidth / 2,
-                top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
-            }}>
-                {maximizeIcon}
-                {titleArea}
-                {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
-                    <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
-                        {"_"}
-                    </div>}
-                <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}>
-                    {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+            {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
+                <div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
+                    width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                    height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+                    left: bounds.x - this._resizeBorderWidth / 2,
+                    top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
+                }}>
+                    {maximizeIcon}
+                    {titleArea}
+                    {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
+                        <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
+                            {"_"}
+                        </div>}
+                    <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}>
+                        {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+                    </div>
+                    <div id="documentDecorations-rotation" className="documentDecorations-rotation"
+                        onPointerDown={this.onRotateDown}> ⟲ </div>
+                    <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-centerCont"></div>
+                    <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
+                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+                        <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+                            title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
+                            <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+                        </div>}
+                    <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
+                        onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
+
+                </div >
+                <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
+                    <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
                 </div>
-                <div id="documentDecorations-rotation" className="documentDecorations-rotation"
-                    onPointerDown={this.onRotateDown}> ⟲ </div>
-                <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-centerCont"></div>
-                <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
-                    onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                    <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
-                        title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
-                        <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
-                    </div>}
-                <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
-                    onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
-
-            </div >
-            <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
-                <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
-            </div>
+            </>}
         </div >
         );
     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index e3546dece..c85849adb 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -20,6 +20,7 @@ import { MainView } from "./MainView";
 import { DocumentView } from "./nodes/DocumentView";
 import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
 import PDFMenu from "./pdf/PDFMenu";
+import { ContextMenu } from "./ContextMenu";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -81,16 +82,17 @@ export default class KeyManager {
                 DocumentLinksButton.StartLink = undefined;
                 const main = MainView.Instance;
                 Doc.SetSelectedTool(InkTool.None);
+                var doDeselect = true;
                 if (main.isPointerDown) {
                     DragManager.AbortDrag();
                 } else {
                     if (CollectionDockingView.Instance.HasFullScreen()) {
                         CollectionDockingView.Instance.CloseFullScreen();
                     } else {
-                        SelectionManager.DeselectAll();
+                        doDeselect = !ContextMenu.Instance.closeMenu();
                     }
                 }
-                SelectionManager.DeselectAll();
+                doDeselect && SelectionManager.DeselectAll();
                 DictationManager.Controls.stop();
                 // RecommendationsBox.Instance.closeMenu();
                 GoogleAuthenticationManager.Instance.cancel();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d107db86b..95c4898cc 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -225,7 +225,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     const res = addedDocs.length ? this.addDocument(addedDocs) : true;
-                    added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res;
+                    added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document) || de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res;
                 } else {
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index dab82185a..705fbdd34 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -141,15 +141,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             } else {
                 added.map(doc => {
                     const context = Cast(doc.context, Doc, null);
-                    if (context && (context?.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
-                        const pushpin = Docs.Create.FreeformDocument([], {
-                            title: "pushpin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null),
-                            _width: 10, _height: 10, _backgroundColor: "red", isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
+                    if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG) &&
+                        !DocListCast(doc.links).some(d => d.isPushpin)) {
+                        const pushpin = Docs.Create.FontIconDocument({
+                            icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7",
+                            _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
                         });
                         Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin);
-                        DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin");
+                        const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin");
                         const first = DocListCast(pushpin.links).find(d => d instanceof Doc);
                         first && (first.hidden = true);
+                        pushpinLink && (Doc.GetProto(pushpinLink).isPushpin = true);
                         doc.displayTimecode = undefined;
                     }
                     doc.context = this.props.Document;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 21b6d8310..09eeaee36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1169,8 +1169,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     render() {
-        if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
         if (!(this.props.Document instanceof Doc)) return (null);
+        if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
+        if (this.props.Document.hidden) return (null);
         const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
         const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
         const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index cf0b16c7c..5e8dd2497 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { createSchema, makeInterface } from '../../../fields/Schema';
 import { DocComponent } from '../DocComponent';
 import './FontIconBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast } from '../../../fields/Types';
+import { StrCast, Cast, NumCast } from '../../../fields/Types';
 import { Utils } from "../../../Utils";
 import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
 import { Doc } from '../../../fields/Doc';
@@ -59,13 +59,14 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
     render() {
         const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
-        const referenceLayout = Doc.Layout(referenceDoc);
+        const refLayout = Doc.Layout(referenceDoc);
         return <button className="fontIconBox-outerDiv" title={StrCast(this.layoutDoc.title)} ref={this._ref} onContextMenu={this.specificContextMenu}
             style={{
-                background: StrCast(referenceLayout.backgroundColor),
+                padding: Cast(this.layoutDoc._xPadding, "number", null),
+                background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
                 boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
             }}>
-            <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" />
+            <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
             {!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>}
         </button>;
     }
-- 
cgit v1.2.3-70-g09d2


From a3ce7e180110df7e5908b93418b2dbff80aa89b5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 Jul 2020 02:50:28 -0400
Subject: added pasting of snipped images (mac at least)

---
 src/Utils.ts                                       | 16 ++++++
 src/client/documents/Documents.ts                  | 30 ++++++++++-
 src/client/views/PreviewCursor.tsx                 | 21 ++++++--
 src/client/views/collections/CollectionSubView.tsx | 59 +++++-----------------
 4 files changed, 75 insertions(+), 51 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index dba802f98..a01a94134 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -7,6 +7,22 @@ import { ColorState } from 'react-color';
 export namespace Utils {
     export let DRAG_THRESHOLD = 4;
 
+    export function readUploadedFileAsText(inputFile: File) {
+        const temporaryFileReader = new FileReader();
+
+        return new Promise((resolve, reject) => {
+            temporaryFileReader.onerror = () => {
+                temporaryFileReader.abort();
+                reject(new DOMException("Problem parsing input file."));
+            };
+
+            temporaryFileReader.onload = () => {
+                resolve(temporaryFileReader.result);
+            };
+            temporaryFileReader.readAsText(inputFile);
+        });
+    }
+
     export function GenerateGuid(): string {
         return v4();
     }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4c27cf47d..85da621e0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,5 +1,5 @@
 import { runInAction } from "mobx";
-import { extname } from "path";
+import { extname, basename } from "path";
 import { DateField } from "../../fields/DateField";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
 import { HtmlField } from "../../fields/HtmlField";
@@ -48,6 +48,8 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
 import { RecommendationsBox } from "../views/RecommendationsBox";
 import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
+import { Networking } from "../Network";
+import { Upload } from "../../server/SharedMediaTypes";
 const path = require('path');
 
 export interface DocumentOptions {
@@ -1104,6 +1106,32 @@ export namespace DocUtils {
         });
         return optionsCollection;
     }
+
+    export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
+        const generatedDocuments: Doc[] = [];
+        for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
+            if (result instanceof Error) {
+                alert(`Upload failed: ${result.message}`);
+                return [];
+            }
+            const full = { ...options, _width: 400, title: name };
+            const pathname = Utils.prepend(result.accessPaths.agnostic.client);
+            const doc = await DocUtils.DocumentFromType(type, pathname, full);
+            if (!doc) {
+                continue;
+            }
+            const proto = Doc.GetProto(doc);
+            proto.text = result.rawText;
+            proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+            if (Upload.isImageInformation(result)) {
+                proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400;
+                proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight);
+                proto.contentSize = result.contentSize;
+            }
+            generatedDocuments.push(doc);
+        }
+        return generatedDocuments;
+    }
 }
 
 Scripting.addGlobal("Docs", Docs);
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 1ab99881d..6583589f3 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -3,13 +3,18 @@ import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
 import "./PreviewCursor.scss";
-import { Docs } from '../documents/Documents';
+import { Docs, DocUtils } from '../documents/Documents';
 import { Doc } from '../../fields/Doc';
 import { Transform } from "../util/Transform";
 import { DocServer } from '../DocServer';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
 import { NumCast } from '../../fields/Types';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import * as rp from 'request-promise';
+import { Utils } from '../../Utils';
+import { Networking } from '../Network';
+import { Upload } from '../../server/SharedMediaTypes';
+import { basename } from 'path';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
@@ -26,7 +31,7 @@ export class PreviewCursor extends React.Component<{}> {
         document.addEventListener("paste", this.paste);
     }
 
-    paste = (e: ClipboardEvent) => {
+    paste = async (e: ClipboardEvent) => {
         if (PreviewCursor.Visible && e.clipboardData) {
             const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
             runInAction(() => PreviewCursor.Visible = false);
@@ -104,6 +109,16 @@ export class PreviewCursor extends React.Component<{}> {
                         x: newPoint[0],
                         y: newPoint[1],
                     })))();
+                } else if (e.clipboardData.items.length) {
+                    const batch = UndoManager.StartBatch("collection view drop");
+                    const files: File[] = [];
+                    for (let i = 0; i < e.clipboardData.items.length; i++) {
+                        const file = e.clipboardData.items[i].getAsFile();
+                        file && files.push(file);
+                    }
+                    const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
+                    generatedDocuments.forEach(PreviewCursor._addDocument);
+                    batch.end();
                 }
         }
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 95c4898cc..ecd16d22f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -238,21 +238,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
             return false;
         }
-        readUploadedFileAsText = (inputFile: File) => {
-            const temporaryFileReader = new FileReader();
 
-            return new Promise((resolve, reject) => {
-                temporaryFileReader.onerror = () => {
-                    temporaryFileReader.abort();
-                    reject(new DOMException("Problem parsing input file."));
-                };
-
-                temporaryFileReader.onload = () => {
-                    resolve(temporaryFileReader.result);
-                };
-                temporaryFileReader.readAsText(inputFile);
-            });
-        }
         @undoBatch
         @action
         protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
@@ -271,8 +257,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
 
             e.stopPropagation();
             e.preventDefault();
-            const { addDocument } = this;
-            if (!addDocument) {
+            if (!this.addDocument) {
                 alert("this.props.addDocument does not exist. Aborting drop operation.");
                 return;
             }
@@ -286,14 +271,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
                                     if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && addDocument(f);
+                                    (f instanceof Doc) && this.addDocument(f);
                                 }
                             });
                         } else {
-                            addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
+                            this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
                         }
                     } else if (text) {
-                        addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
+                        this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
                     }
                     return;
                 }
@@ -313,7 +298,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                         if (source.startsWith("http")) {
                             const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
                             ImageUtils.ExtractExif(doc);
-                            addDocument(doc);
+                            this.addDocument(doc);
                         }
                         return;
                     } else {
@@ -360,7 +345,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             if (text) {
                 if (text.includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
                     const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0];
-                    addDocument(Docs.Create.VideoDocument(url, {
+                    this.addDocument(Docs.Create.VideoDocument(url, {
                         ...options,
                         title: url,
                         _width: 400,
@@ -413,10 +398,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     const file = item.getAsFile();
                     file?.type && files.push(file);
 
-                    file?.type === "application/json" && this.readUploadedFileAsText(file).then(result => {
+                    file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => {
                         console.log(result);
                         const json = JSON.parse(result as string);
-                        addDocument(Docs.Create.TreeDocument(
+                        this.addDocument(Docs.Create.TreeDocument(
                             json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => {
                                 const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 });
                                 const proto = Doc.GetProto(label);
@@ -428,38 +413,18 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     });
                 }
             }
-            for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
-                if (result instanceof Error) {
-                    alert(`Upload failed: ${result.message}`);
-                    return;
-                }
-                const full = { ...options, _width: 400, title: name };
-                const pathname = Utils.prepend(result.accessPaths.agnostic.client);
-                const doc = await DocUtils.DocumentFromType(type, pathname, full);
-                if (!doc) {
-                    continue;
-                }
-                const proto = Doc.GetProto(doc);
-                proto.text = result.rawText;
-                proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
-                if (Upload.isImageInformation(result)) {
-                    proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400;
-                    proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight);
-                    proto.contentSize = result.contentSize;
-                }
-                generatedDocuments.push(doc);
-            }
+            generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
             if (generatedDocuments.length) {
                 const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d));
                 if (set) {
-                    addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                    this.addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
                 } else {
-                    generatedDocuments.forEach(addDocument);
+                    generatedDocuments.forEach(this.addDocument);
                 }
                 completed?.();
             } else {
                 if (text && !text.includes("https://")) {
-                    addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+                    this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
                 }
             }
             batch.end();
-- 
cgit v1.2.3-70-g09d2


From eff0c84e6f42bd6be1c58f1009d8d766bcf0a819 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 Jul 2020 15:42:22 -0400
Subject: fixed warnings

---
 src/client/util/CurrentUserUtils.ts                       |  2 +-
 src/client/views/collections/CollectionSchemaView.tsx     |  8 ++++----
 src/client/views/collections/CollectionSubView.tsx        |  1 -
 src/client/views/collections/CollectionViewChromes.tsx    |  6 +++---
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 14 +++++---------
 src/client/views/nodes/formattedText/RichTextMenu.tsx     |  8 ++++----
 src/fields/Doc.ts                                         |  2 +-
 7 files changed, 18 insertions(+), 23 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index ef216001c..40bc24917 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -368,7 +368,7 @@ export class CurrentUserUtils {
             doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300 });
         }
         if (doc.emptyScript === undefined) {
-            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script" })
+            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script" });
         }
         if (doc.emptyDocHolder === undefined) {
             doc.emptyDocHolder = Docs.Create.DocumentDocument(
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 23e8a4d49..3c42a2f1c 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -581,14 +581,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     onZoomMenu = (e: React.WheelEvent) => {
         this.props.active(true) && e.stopPropagation();
         if (this.menuCoordinates[0] > e.screenX) {
-            this.menuCoordinates[0] -= e.screenX //* this.scale;
+            this.menuCoordinates[0] -= e.screenX; //* this.scale;
         } else {
-            this.menuCoordinates[0] += e.screenX //* this.scale;
+            this.menuCoordinates[0] += e.screenX; //* this.scale;
         }
         if (this.menuCoordinates[1] > e.screenY) {
-            this.menuCoordinates[1] -= e.screenY //* this.scale;
+            this.menuCoordinates[1] -= e.screenY; //* this.scale;
         } else {
-            this.menuCoordinates[1] += e.screenY //* this.scale;
+            this.menuCoordinates[1] += e.screenY; //* this.scale;
         }
     }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ecd16d22f..ed8535ecb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,6 @@ import { ScriptField } from "../../../fields/ScriptField";
 import { WebField } from "../../../fields/URLField";
 import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Upload } from "../../../server/SharedMediaTypes";
 import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Networking } from "../../Network";
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index ab0df88f8..4e91a2928 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -452,9 +452,9 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
     @action toggleSort = () => {
         this.props.CollectionView.props.Document._columnsSort =
-        this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" :
-            this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending";
-    };
+            this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" :
+                this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending";
+    }
     @action resetValue = () => { this._currentKey = this.pivotField; };
 
     render() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7e5509e85..664141607 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -68,15 +68,10 @@ export interface FormattedTextBoxProps {
     xMargin?: number;   // used to override document's settings for xMargin --- see CollectionCarouselView
     yMargin?: number;
 }
-
-const richTextSchema = createSchema({
-    documentText: "string",
-});
-
 export const GoogleRef = "googleDocId";
 
-type RichTextDocument = makeInterface<[typeof richTextSchema, typeof documentSchema]>;
-const RichTextDocument = makeInterface(richTextSchema, documentSchema);
+type RichTextDocument = makeInterface<[typeof documentSchema]>;
+const RichTextDocument = makeInterface(documentSchema);
 
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
@@ -661,7 +656,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             incomingValue => {
                 if (incomingValue !== undefined && this._editorView && !this._applyingChange) {
                     const updatedState = JSON.parse(incomingValue);
-                    if (JSON.stringify(this._editorView!.state.toJSON()) !== JSON.stringify(updatedState)) {
+                    if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
                         this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
                         this.tryUpdateHeight();
                     }
@@ -1293,7 +1288,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
                         pointerEvents: interactive ? undefined : "none",
                         fontSize: Cast(this.layoutDoc._fontSize, "number", null),
-                        fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit")
+                        fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
+                        transition: "opacity 1s"
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyPress}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 57719a8eb..0fcddfc43 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -149,7 +149,7 @@ export default class RichTextMenu extends AntimodeMenu {
         this._reaction?.();
     }
 
-    public delayHide = () => { this._delayHide = true; }
+    public delayHide = () => this._delayHide = true;
 
     @action
     changeView(view: EditorView) {
@@ -599,7 +599,7 @@ export default class RichTextMenu extends AntimodeMenu {
 
     @action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; }
     @action setActiveColor(color: string) { this.activeFontColor = color; }
-    get TextView() { return (this.view as any).TextView as FormattedTextBox }
+    get TextView() { return (this.view as any).TextView as FormattedTextBox; }
 
     createColorButton() {
         const self = this;
@@ -608,7 +608,7 @@ export default class RichTextMenu extends AntimodeMenu {
             e.stopPropagation();
             self.TextView.endUndoTypingBatch();
             UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
-            self.TextView.EditorView!.focus()
+            self.TextView.EditorView!.focus();
         }
         function changeColor(e: React.PointerEvent, color: string) {
             e.preventDefault();
@@ -616,7 +616,7 @@ export default class RichTextMenu extends AntimodeMenu {
             self.setActiveColor(color);
             self.TextView.endUndoTypingBatch();
             UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
-            self.TextView.EditorView!.focus()
+            self.TextView.EditorView!.focus();
         }
 
         const button =
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e4d11dd4d..4a35ef018 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -965,7 +965,7 @@ export namespace Doc {
                     container._docFilters = new List<string>(docFilters);
                 }
             }
-        })
+        });
     }
     export function readDocRangeFilter(doc: Doc, key: string) {
         const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []);
-- 
cgit v1.2.3-70-g09d2

