From 0bc38e1ca78a5a2ee21d99ef511f2f744d8c67bb Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Sun, 4 Apr 2021 01:55:12 -0400
Subject: added datadoc print to console

---
 src/client/views/nodes/DocumentView.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26cf52f17..3870cbed0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -740,6 +740,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
         helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
         !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+        !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
         cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
 
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
-- 
cgit v1.2.3-70-g09d2


From d1e32902d0dabf92dd06d5aa9a139368eff17798 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Jun 2021 18:40:28 -0400
Subject: fixed positioning of blue anchor dot when showing link anchor lines

---
 src/client/views/nodes/DocumentView.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b861669f8..5646a9790 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils, returnTrue } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -846,6 +846,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     dontRegisterView={false}
+                    fitWidth={returnTrue}
                     styleProvider={this.anchorStyleProvider}
                     removeDocument={this.hideLinkAnchor}
                     LayoutTemplate={undefined}
-- 
cgit v1.2.3-70-g09d2


From 405114b0a2440939e89052539eb91f479e8d4176 Mon Sep 17 00:00:00 2001
From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com>
Date: Wed, 28 Jul 2021 16:07:08 -0700
Subject: fix tree view collection drop shadow

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

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 60fa462ad..2b0d8fe91 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -979,7 +979,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
-        const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
+        const boxShadow = this.props.treeViewDoc ? undefined : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
         return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
-- 
cgit v1.2.3-70-g09d2


From f59495f0aa5ba65b358f9430f55c653ed0fc3d70 Mon Sep 17 00:00:00 2001
From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com>
Date: Sat, 31 Jul 2021 22:26:48 -0700
Subject: small UI changes

---
 src/client/views/global/globalCssVariables.scss | 22 +++++++++++-----------
 src/client/views/nodes/DocumentView.tsx         |  4 ++--
 2 files changed, 13 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index 1b881ba43..7556f8b8a 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -1,15 +1,15 @@
-@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700");
+@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
 // colors
 $white: #ffffff;
-$light-gray:#dfdfdf;
-$medium-gray: #9F9F9F;
+$light-gray: #dfdfdf;
+$medium-gray: #9f9f9f;
 $dark-gray: #323232;
 $black: #000000;
 
-$light-blue: #BDDDF5;
-$medium-blue: #4476F7;
-$pink: #E0217D;
-$yellow: #F5D747;
+$light-blue: #bdddf5;
+$medium-blue: #4476f7;
+$pink: #e0217d;
+$yellow: #f5d747;
 
 $close-red: #e48282;
 
@@ -24,7 +24,7 @@ $large-padding: 32px;
 $icon-size: 28px;
 
 // fonts
-$sans-serif: "Noto Sans", sans-serif;
+$sans-serif: "Roboto", sans-serif;
 $large-header: 16px;
 $body-text: 12px;
 $small-text: 9px;
@@ -41,7 +41,7 @@ $contextMenu-zindex: 100000; // context menu shows up over everything
 $radialMenu-zindex: 100000; // context menu shows up over everything
 
 // borders
-$standard-border: solid 1px #9F9F9F;
+$standard-border: solid 1px #9f9f9f;
 
 $searchpanel-height: 32px;
 $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
@@ -49,7 +49,7 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
 $COLLECTION_BORDER_WIDTH: 0;
 $SCHEMA_DIVIDER_WIDTH: 4;
-$MINIMIZED_ICON_SIZE:24;
+$MINIMIZED_ICON_SIZE: 24;
 $MAX_ROW_HEIGHT: 44px;
 $DFLT_IMAGE_NATIVE_DIM: 900px;
 $MENU_PANEL_WIDTH: 60px;
@@ -67,4 +67,4 @@ $TREE_BULLET_WIDTH: 20px;
     DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
     MENU_PANEL_WIDTH: $MENU_PANEL_WIDTH;
     TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index df4cba01b..97ad37a08 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -971,7 +971,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
         const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
-            ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
+            ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
         let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
@@ -979,7 +979,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
-        const boxShadow = this.props.treeViewDoc ? undefined : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
+        const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
         return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
-- 
cgit v1.2.3-70-g09d2


From 0546ecf205b7d2b76f341a7157beebf95fb888a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 1 Aug 2021 22:43:46 -0400
Subject: made url server references relative.

---
 src/Utils.ts                                       |  1 -
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  2 +-
 src/client/documents/Documents.ts                  | 90 ++--------------------
 src/client/util/HypothesisUtils.ts                 |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  6 +-
 src/client/views/nodes/FieldView.tsx               |  6 +-
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  4 +-
 src/client/views/nodes/PDFBox.tsx                  | 24 ------
 src/client/views/nodes/ScreenshotBox.tsx           |  4 +-
 src/client/views/nodes/VideoBox.tsx                |  8 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  6 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  8 +-
 src/client/views/pdf/AnchorMenu.tsx                |  1 -
 src/fields/Doc.ts                                  | 12 ++-
 src/fields/URLField.ts                             | 15 +++-
 src/mobile/ImageUpload.tsx                         |  2 +-
 src/server/server_Initialization.ts                |  3 +-
 22 files changed, 58 insertions(+), 146 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index d87c3cc6b..194c38a6f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -67,7 +67,6 @@ export namespace Utils {
     export function prepend(extension: string): string {
         return window.location.origin + extension;
     }
-
     export function fileUrl(filename: string): string {
         return prepend(`/files/${filename}`);
     }
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 899e65a16..ff9460b62 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -285,7 +285,7 @@ export namespace GooglePhotos {
             const photos = await endpoint();
             const albumId = StrCast(collection.albumId);
             if (albumId && albumId.length) {
-                const enrichment = new photos.TextEnrichment(content || Utils.prepend("/doc/" + collection[Id]));
+                const enrichment = new photos.TextEnrichment(content || Doc.globalServerPath(collection));
                 const position = new photos.AlbumPosition(photos.AlbumPosition.POSITIONS.FIRST_IN_ALBUM);
                 const enrichmentItem = await photos.albums.addEnrichment(albumId, enrichment, position);
                 if (enrichmentItem) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e863b4198..ac52b0acf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -549,84 +549,6 @@ export namespace Docs {
      */
     export namespace Create {
 
-        /**
-         * Synchronously returns a collection into which
-         * the device documents will be put. This is initially empty,
-         * but gets populated by updates from the web socket. When everything is over,
-         * this function cleans up after itself.
-         * s
-         * Look at Websocket.ts for the server-side counterpart to this
-         * function.
-         */
-        export function Buxton() {
-            let responded = false;
-            const loading = new Doc;
-            loading.title = "Please wait for the import script...";
-            const parent = TreeDocument([loading], {
-                title: "The Buxton Collection",
-                _width: 400,
-                _height: 400
-            });
-            const parentProto = Doc.GetProto(parent);
-            const { _socket } = DocServer;
-
-            // just in case, clean up
-            _socket.off(MessageStore.BuxtonDocumentResult.Message);
-            _socket.off(MessageStore.BuxtonImportComplete.Message);
-
-            // this is where the client handles the receipt of a new valid parsed document
-            Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, invalid: errors }) => {
-                if (!responded) {
-                    responded = true;
-                    parentProto.data = new List<Doc>();
-                }
-                if (device) {
-                    const { title, __images, additionalMedia } = device;
-                    delete device.__images;
-                    delete device.additionalMedia;
-                    const { ImageDocument, StackingDocument } = Docs.Create;
-                    const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight }));
-                    const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => {
-                        const imageDoc = ImageDocument(url, {
-                            title: `image${i}.${extname(url)}`,
-                            _nativeWidth: nativeWidth,
-                            _nativeHeight: nativeHeight
-                        });
-                        const media = additionalMedia[i];
-                        if (media) {
-                            for (const key of Object.keys(media)) {
-                                imageDoc[`additionalMedia_${key}`] = Utils.prepend(`/files/${key}/buxton/${media[key]}`);
-                            }
-                        }
-                        return imageDoc;
-                    });
-                    // the main document we create
-                    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) } });
-                    Doc.AddDocToList(parentProto, "data", doc);
-                } else if (errors) {
-                    console.log("Documents:" + errors);
-                } else {
-                    alert("A Buxton document import was completely empty (??)");
-                }
-            });
-
-            // when the import is complete, we stop listening for these creation
-            // and termination events and alert the user
-            Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => {
-                _socket.off(MessageStore.BuxtonDocumentResult.Message);
-                _socket.off(MessageStore.BuxtonImportComplete.Message);
-                alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`);
-            });
-            const startTime = Date.now();
-            Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); // signal the server to start importing
-            return parent; // synchronously return the collection, to be populateds
-        }
-
-        Scripting.addGlobal(Buxton);
-
         /**
          * This function receives the relevant document prototype and uses
          * it to create a new of that base-level prototype, or the
@@ -675,7 +597,7 @@ export namespace Docs {
         }
 
         export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            const imgField = new ImageField(new URL(url));
+            const imgField = new ImageField(url);
             return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
         }
 
@@ -689,11 +611,11 @@ export namespace Docs {
         }
 
         export function VideoDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options);
+            return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options);
         }
 
         export function YoutubeDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options);
+            return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options);
         }
 
         export function WebCamDocument(url: string, options: DocumentOptions = {}) {
@@ -709,7 +631,7 @@ export namespace Docs {
         }
 
         export function AudioDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)),
+            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url),
                 { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
         }
 
@@ -782,11 +704,11 @@ export namespace Docs {
         }
 
         export function PdfDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(new URL(url)), options);
+            return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options);
         }
 
         export function WebDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, options);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options);
         }
 
         export function HtmlDocument(html: string, options: DocumentOptions = {}) {
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 8ddfce772..635673025 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -126,7 +126,7 @@ export namespace Hypothesis {
         });
 
         const annotationId = StrCast(linkDoc.annotationId);
-        const linkUrl = Utils.prepend("/doc/" + sourceDoc[Id]);
+        const linkUrl = Doc.globalServerPath(sourceDoc);
         const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful
             !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
                 detail: { targetUrl: linkUrl, id: annotationId },
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a5d27f038..0d9b64d24 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -303,7 +303,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     } else {
                         const path = window.location.origin + "/doc/";
                         if (text.startsWith(path)) {
-                            const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                            const docid = text.replace(Doc.globalServerPath(), "").split("?")[0];
                             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
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 339163510..08b5e6bac 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -37,7 +37,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _focusRangeFilters: Opt<string[]>;
 
     getAnchor = () => {
-        const anchor = Docs.Create.HTMLAnchorDocument({
+        const anchor = Docs.Create.HTMLAnchorDocument([], {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
             annotationOn: this.rootDoc
         });
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index a2e36f12e..82bad971d 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -196,7 +196,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
             if (!(result instanceof Error)) {
-                this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+                this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
             }
         };
         this._recordStart = new Date().getTime();
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index ddc36daa1..aa3f10188 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -194,7 +194,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
                 Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
                 Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
-                const dashHyperlink = Utils.prepend("/doc/" + (startIsAnnotation ? endLink[Id] : startLink[Id]));
+                const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
                 Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
                     (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
             }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 60fa462ad..b8ccc4712 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -746,7 +746,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
                         moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
                     }
-                    moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
+                    moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" });
                 }
             }
 
@@ -760,7 +760,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
             !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
             !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
@@ -885,7 +885,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             recorder.ondataavailable = async (e: any) => {
                 const [{ result }] = await Networking.UploadFilesToServer(e.data);
                 if (!(result instanceof Error)) {
-                    const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+                    const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 });
                     audioDoc.treeViewExpandedView = "layout";
                     const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc));
                     if (audioAnnos === undefined) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 86250c9d1..ebbc1138a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -64,9 +64,9 @@ export class FieldView extends React.Component<FieldViewProps> {
         // else if (field instaceof PresBox) {
         //    return <PresBox {...this.props} />;
         // }
-        else if (field instanceof VideoField) {
-            return <VideoBox {...this.props} />;
-        }
+        // else if (field instanceof VideoField) {
+        //     return <VideoBox {...this.props} />;
+        // }
         // else if (field instanceof AudioField) {
         //     return <AudioBox {...this.props} />;
         //}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index cfd43bb62..2c0106960 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -238,7 +238,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     let succeeded = true;
                     let data: ImageField | undefined;
                     try {
-                        data = new ImageField(Utils.prepend(accessPaths.agnostic.client));
+                        data = new ImageField(accessPaths.agnostic.client);
                     } catch {
                         succeeded = false;
                     }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index b73fb10df..126a37eb8 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -72,14 +72,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @computed get href() {
         if (this.props.hrefs?.length) {
             const href = this.props.hrefs[this._hrefInd];
-            if (href.indexOf(Utils.prepend("/doc/")) !== 0) {  // link to a web page URL -- try to show a preview
+            if (href.indexOf(Doc.localServerPath()) !== 0) {  // link to a web page URL -- try to show a preview
                 if (href.startsWith("https://en.wikipedia.org/wiki/")) {
                     wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
                 } else {
                     setTimeout(action(() => this._toolTipText = "url => " + href));
                 }
             } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
-                const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0];
                 anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => {
                     if (anchor instanceof Doc && DocListCast(anchor.links).length) {
                         this._linkDoc = DocListCast(anchor.links)[0];
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8f61e252b..0b451e2b4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -53,30 +53,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
             else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf));
         }
-
-        const backup = "oldPath";
-        const href = this.pdfUrl?.url.href;
-        if (href) {
-            const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g;
-            const matches = pathCorrectionTest.exec(href);
-            // console.log("\nHere's the { url } being fed into the outer regex:");
-            // console.log(href);
-            // console.log("And here's the 'properPath' build from the captured filename:\n");
-            if (matches !== null && href.startsWith(window.location.origin)) {
-                const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`);
-                //console.log(properPath);
-                if (!properPath.includes(href)) {
-                    console.log(`The two (url and proper path) were not equal`);
-                    const proto = Doc.GetProto(this.props.Document);
-                    proto[this.props.fieldKey] = new PdfField(properPath);
-                    proto[backup] = href;
-                } else {
-                    //console.log(`The two (url and proper path) were equal`);
-                }
-            } else {
-                console.log("Outer matches was null!");
-            }
-        }
     }
 
     componentWillUnmount() { this._selectReactionDisposer?.(); }
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 700f8a7d3..0e235a62d 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -227,7 +227,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             this._audioRec.onstop = async (e: any) => {
                 const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
                 if (!(result instanceof Error)) {
-                    this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+                    this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
                 }
             };
             this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
@@ -244,7 +244,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
                     this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
                     this.layoutDoc._fitWidth = undefined;
-                    this.dataDoc[this.props.fieldKey] = new VideoField(Utils.prepend(result.accessPaths.agnostic.client));
+                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
                 } else alert("video conversion failed");
             };
             this._audioRec.start();
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fc08a2302..ce45c01e6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -75,10 +75,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined) || this.rootDoc;
     }
 
-    choosePath(url: string) {
-        return url.indexOf(window.location.origin) === -1 ? Utils.CorsProxy(url) : url;
-    }
-
     videoLoad = () => {
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
         Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
@@ -182,8 +178,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
     }
 
-    private createRealSummaryLink = (relative: string, downX?: number, downY?: number) => {
-        const url = this.choosePath(Utils.prepend(relative));
+    private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
+        const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
         const width = this.layoutDoc._width || 1;
         const height = this.layoutDoc._height || 0;
         const imageSummary = Docs.Create.ImageDocument(url, {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 140d39929..f7e9ee028 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
                 const anchor = Docs.Create.TextanchorDocument();
                 const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!;
-                const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }];
+                const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }];
                 const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location });
                 tr = tr.addMark(flattened[i].from, flattened[i].to, link);
             });
@@ -705,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             if (sel.from !== sel.to) {
                 const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) });
-                const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]);
+                const href = targetHref ?? Doc.localServerPath(anchor);
                 if (anchor !== anchorDoc) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
                     if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
@@ -1042,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type.name === "link");
-            const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
+            const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }];
             const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
             marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
             return node.mark(marks);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index a6f8ff2e2..fb4114023 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -821,8 +821,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         if (link) {
             const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined;
             if (href) {
-                if (href.indexOf(Utils.prepend("/doc/")) === 0) {
-                    const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                if (href.indexOf(Doc.localServerPath()) === 0) {
+                    const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0];
                     if (linkclicked) {
                         const linkDoc = await DocServer.GetRefField(linkclicked);
                         if (linkDoc instanceof Doc) {
@@ -864,8 +864,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 const allAnchors = linkAnchor.attrs.allAnchors.slice();
                 this.TextView.RemoveAnchorFromSelection(allAnchors);
                 // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected.
-                allAnchors.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => {
-                    const anchorId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => {
+                    const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0];
                     anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc));
                 });
             }
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 70ca19842..55816ed52 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -85,7 +85,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @action
     toggleLinkPopup = (e: React.MouseEvent) => {
         //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button
-        console.log(window.getSelection().toString())
         //change popup visibility field to visible
         this._showLinkPopup = !this._showLinkPopup;
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 111fd3f0d..a7e5d8541 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -596,7 +596,7 @@ export namespace Doc {
                 const mapped = cloneMap.get(id);
                 return href + (mapped ? mapped[Id] : id);
             };
-            const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
+            const regex = `(${Doc.localServerPath()})([^"]*)`;
             const re = new RegExp(regex, "g");
             copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
         });
@@ -896,6 +896,16 @@ export namespace Doc {
         return true;
     }
 
+
+    // converts a document id to a url path on the server
+    export function globalServerPath(doc: Doc | string = ""): string {
+        return Utils.prepend("/doc/" + (doc instanceof Doc ? doc[Id] : doc));
+    }
+    // converts a document id to a url path on the server
+    export function localServerPath(doc?: Doc): string {
+        return "/doc/" + (doc ? doc[Id] : "");
+    }
+
     export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
         const doc2Layout = Doc.Layout(doc2);
         const doc1Layout = Doc.Layout(doc1);
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index fb71160ca..d96e8a70a 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -3,14 +3,17 @@ import { serializable, custom } from "serializr";
 import { ObjectField } from "./ObjectField";
 import { ToScriptString, ToString, Copy } from "./FieldSymbols";
 import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { Utils } from "../Utils";
 
 function url() {
     return custom(
         function (value: URL) {
-            return value.href;
+            return value.origin === window.location.origin ?
+                value.pathname :
+                value.href;
         },
         function (jsonValue: string) {
-            return new URL(jsonValue);
+            return new URL(jsonValue, window.location.origin);
         }
     );
 }
@@ -24,15 +27,21 @@ export abstract class URLField extends ObjectField {
     constructor(url: URL | string) {
         super();
         if (typeof url === "string") {
-            url = new URL(url);
+            url = url.startsWith("http") ? new URL(url) : new URL(url, window.location.origin);
         }
         this.url = url;
     }
 
     [ToScriptString]() {
+        if (Utils.prepend(this.url.pathname) === this.url.href) {
+            return `new ${this.constructor.name}("${this.url.pathname}")`;
+        }
         return `new ${this.constructor.name}("${this.url.href}")`;
     }
     [ToString]() {
+        if (Utils.prepend(this.url.pathname) === this.url.href) {
+            return this.url.pathname;
+        }
         return this.url.href;
     }
 
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 98696496f..f910d765e 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -50,7 +50,7 @@ export class Uploader extends React.Component<ImageUploadProps> {
                             if (result instanceof Error) {
                                 return;
                             }
-                            const path = Utils.prepend(result.accessPaths.agnostic.client);
+                            const path = result.accessPaths.agnostic.client;
                             let doc = null;
                             // Case 1: File is a video
                             if (file.type === "video/mp4") {
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index e40f2b8e5..0f4a067fc 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -142,8 +142,9 @@ function registerCorsProxy(server: express.Express) {
     const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
     server.use("/corsProxy", async (req, res) => {
 
-        const requrl = decodeURIComponent(req.url.substring(1));
         const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : "";
+        const requrlraw = decodeURIComponent(req.url.substring(1));
+        const requrl = requrlraw.startsWith("/") ? referer + requrlraw : requrlraw;
         // cors weirdness here... 
         // if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/<path> and the requested url path was relative, 
         // then we redirect again to the cors referer and just add the relative path.
-- 
cgit v1.2.3-70-g09d2


From 0e8aef275346b4ba3bc1bb91fda17a335c307bf1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 3 Aug 2021 13:09:48 -0400
Subject: fixed using viewSpecs when following links to set filters/viewType
 properly.

---
 src/client/util/DocumentManager.ts                      |  2 +-
 src/client/views/collections/CollectionSubView.tsx      |  6 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx       | 17 ++++++++++++-----
 src/client/views/nodes/DocumentView.tsx                 | 14 ++++++++------
 4 files changed, 24 insertions(+), 15 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5b092258a..9c6100249 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -163,7 +163,7 @@ export class DocumentManager {
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext);
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
-        if (!docView && annoContainerView && !focusView) {
+        if (!docView && annoContainerView) {
             annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
         }
         if (focusView) {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0d9b64d24..3b143aeef 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -82,13 +82,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return Cast(this.dataField, listSpec(Doc));
         }
         docFilters = () => {
-            return [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
+            return (this.props.docFilters?.() ?? Cast(this.props.Document._docFilters, listSpec("string"), []));
         }
         docRangeFilters = () => {
-            return [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])];
+            return (this.props.docRangeFilters?.() ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []));
         }
         searchFilterDocs = () => {
-            return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
+            return this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         }
         @computed.struct get childDocs() {
             TraceMobx();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 143d8e070..ecc93285e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -159,8 +159,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.layoutDoc._viewScale = vals.scale;
     }
     freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
-    freeformDocFilters = () => this._focusFilters || this.docFilters();
-    freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters();
+    freeformDocFilters = () => this._focusFilters;
+    freeformRangeDocFilters = () => this._focusRangeFilters;
     reverseNativeScaling = () => this.fitToContent ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
@@ -1197,14 +1197,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (preview) {
             this._focusFilters = StrListCast(Doc.GetProto(anchor).docFilters);
             this._focusRangeFilters = StrListCast(Doc.GetProto(anchor).docRangeFilters);
-        } else if (anchor.pivotField !== undefined) {
-            this.layoutDoc._docFilters = new List<string>(StrListCast(anchor.docFilters));
-            this.layoutDoc._docRangeFilters = new List<string>(StrListCast(anchor.docRangeFilters));
+        } else {
+            if (anchor.docFilters) {
+                this.layoutDoc._docFilters = new List<string>(StrListCast(anchor.docFilters));
+            }
+            if (anchor.docRangeFilters) {
+                this.layoutDoc._docRangeFilters = new List<string>(StrListCast(anchor.docRangeFilters));
+            }
         }
         return 0;
     }
 
     getAnchor = () => {
+        if (this.props.Document.annotationOn) {
+            return this.rootDoc;
+        }
         const anchor = Docs.Create.TextanchorDocument({ title: StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
         const proto = Doc.GetProto(anchor);
         proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f716eb7b1..c218b805e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -64,7 +64,7 @@ export enum ViewAdjustment {
     doNothing = 0
 }
 
-export const ViewSpecPrefix = "_VIEW";  // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
+export const ViewSpecPrefix = "viewSpec";  // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
 
 export interface DocFocusOptions {
     originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab
@@ -105,9 +105,9 @@ export interface DocumentViewSharedProps {
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: () => boolean;
-    docFilters: () => string[];
-    docRangeFilters: () => string[];
-    searchFilterDocs: () => Doc[];
+    docFilters?: () => Opt<string[]>;
+    docRangeFilters?: () => Opt<string[]>;
+    searchFilterDocs?: () => Opt<Doc[]>;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
@@ -421,7 +421,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     focus = (anchor: Doc, options?: DocFocusOptions) => {
         LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
         // copying over _VIEW fields immediately allows the view type to switch to create the right _componentView
-        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]));
+        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
+            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
+        });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here      
@@ -837,7 +839,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
         if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
         // need to use allLinks for RTF since embedded linked text anchors are not rendered with DocumentViews.  All other documents render their anchors with nested DocumentViews so we just need to render the directLinks here
-        const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden);
+        const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
         return filtered.map((link, i) =>
             <div className="documentView-anchorCont" key={i + 1}>
                 <DocumentView {...this.props}
-- 
cgit v1.2.3-70-g09d2


From 70f98c6d4d3e457a05e7c84260bf33580ee0166a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 4 Aug 2021 10:34:25 -0400
Subject: restructured filters again so that filter icons show whether a
 document has a filter or inherits one.  following a link with a view spec
 overrides only locally set filters (not inherited ones).

---
 src/client/views/.DS_Store                         | Bin 10244 -> 10244 bytes
 .../views/collections/CollectionSchemaView.tsx     |   4 +--
 .../views/collections/CollectionStackingView.tsx   |   4 +--
 src/client/views/collections/CollectionSubView.tsx |  29 ++++++++++++---------
 .../views/collections/CollectionTimeView.tsx       |  12 +++------
 src/client/views/collections/CollectionView.tsx    |  14 +++++++---
 src/client/views/collections/TabDocView.tsx        |   9 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   8 ++----
 .../CollectionMulticolumnView.tsx                  |   4 +--
 .../CollectionMultirowView.tsx                     |   4 +--
 .../collectionSchema/CollectionSchemaView.tsx      |   4 +--
 src/client/views/nodes/DocumentView.tsx            |   6 ++---
 12 files changed, 50 insertions(+), 48 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index 33e624ef4..e4ac87aad 100644
Binary files a/src/client/views/.DS_Store and b/src/client/views/.DS_Store differ
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index e1e04915a..380f82813 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -413,8 +413,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     isContentActive={returnTrue}
                     isDocumentActive={returnFalse}
                     ScreenToLocalTransform={this.getPreviewTransform}
-                    docFilters={this.docFilters}
-                    docRangeFilters={this.docRangeFilters}
+                    docFilters={this.childDocFilters}
+                    docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
                     layerProvider={undefined}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7aa8dfd56..b9bc83d49 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -239,10 +239,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={stackedDocTransform}
             focus={this.focusDocument}
-            docFilters={this.docFilters}
+            docFilters={this.childDocFilters}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             hideTitle={this.props.childHideTitle?.()}
-            docRangeFilters={this.docRangeFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3b143aeef..e5f266acf 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -8,7 +8,7 @@ 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 { Utils, returnFalse } from "../../../Utils";
+import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Networking } from "../../Network";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -22,6 +22,7 @@ import ReactLoading from 'react-loading';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
+    SetSubView: (subView: any) => void;
 }
 
 export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
@@ -30,6 +31,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
         protected _mainCont?: HTMLDivElement;
+        @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
+        @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
         protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
@@ -45,6 +48,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             this.createDashEventsTarget(ele);
         }
 
+        componentDidMount() {
+            this.props.SetSubView(this);
+        }
+
         componentWillUnmount() {
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
@@ -81,15 +88,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
-        docFilters = () => {
-            return (this.props.docFilters?.() ?? Cast(this.props.Document._docFilters, listSpec("string"), []));
-        }
-        docRangeFilters = () => {
-            return (this.props.docRangeFilters?.() ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []));
-        }
-        searchFilterDocs = () => {
-            return this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
-        }
+        collectionFilters = () => this._focusFilters ?? Cast(this.props.Document._docFilters, listSpec("string"), []);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+        childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
+        childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
+        IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
+            this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined;
+        searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
@@ -108,8 +113,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
             const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
 
-            const docFilters = this.docFilters();
-            const docRangeFilters = this.docRangeFilters();
+            const docFilters = this.childDocFilters();
+            const docRangeFilters = this.childDocRangeFilters();
             const searchDocs = this.searchFilterDocs();
             if (this.props.Document.dontRegisterView || (!docFilters.length && !docRangeFilters.length && !searchDocs.length)) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 08b5e6bac..292dfd77c 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -32,9 +32,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _collapsed: boolean = false;
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
-    @observable _focusDocFilters: Opt<string[]>; // fields that get overridden by a focus anchor
     @observable _focusPivotField: Opt<string>;
-    @observable _focusRangeFilters: Opt<string[]>;
 
     getAnchor = () => {
         const anchor = Docs.Create.HTMLAnchorDocument([], {
@@ -72,9 +70,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @action
     setViewSpec = (anchor: Doc, preview: boolean) => {
         if (preview) {   // if in preview, then override document's fields with view spec
+            this._focusFilters = StrListCast(Doc.GetProto(anchor).docFilters);
+            this._focusRangeFilters = StrListCast(Doc.GetProto(anchor).docRangeFilters);
             this._focusPivotField = StrCast(anchor.pivotField);
-            this._focusDocFilters = StrListCast(anchor.docFilters);
-            this._focusRangeFilters = StrListCast(anchor.docRangeFilters);
         } else if (anchor.pivotField !== undefined) {  // otherwise set document's fields based on anchor view spec
             this.layoutDoc._prevFilterIndex = 1;
             this.layoutDoc._pivotField = StrCast(anchor.pivotField);
@@ -84,8 +82,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         return 0;
     }
 
-    pivotDocFilters = () => this._focusDocFilters || this.props.docFilters();
-    pivotDocRangeFilters = () => this._focusRangeFilters || this.props.docRangeFilters();
     layoutEngine = () => this._layoutEngine;
     toggleVisibility = action(() => this._collapsed = !this._collapsed);
 
@@ -139,10 +135,8 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : "none" }}
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
-                engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters }}
+                engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
                 fitContentsToDoc={returnTrue}
-                docFilters={this.pivotDocFilters}
-                docRangeFilters={this.pivotDocRangeFilters}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e225c4a11..2718cbbbf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { computed, observable, runInAction } from 'mobx';
+import { computed, observable, runInAction, action } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
@@ -236,13 +236,19 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     * Shows the filter icon if it's a user-created collection which isn't a dashboard and has some docFilters applied on it or on the current dashboard.
     */
     @computed get showFilterIcon() {
-        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && ((StrListCast(this.props.Document._docFilters).length || StrListCast(this.props.Document._docRangeFilters).length || StrListCast(CurrentUserUtils.ActiveDashboard._docFilters).length || StrListCast(CurrentUserUtils.ActiveDashboard._docRangeFilters).length));
+        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered()
     }
 
+    @observable _subView: any = undefined;
+
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
             ...this.props,
+            SetSubView: action((subView: any) => {
+                console.log("Setting sub", subView);
+                return this._subView = subView
+            }),
             addDocument: this.addDocument,
             moveDocument: this.moveDocument,
             removeDocument: this.removeDocument,
@@ -260,8 +266,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             {this.showFilterIcon ?
                 <FontAwesomeIcon icon={"filter"} size="lg"
-                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: '#18c718bd', zIndex: 1 }}
-                    onPointerDown={e => { runInAction(() => CurrentUserUtils.propertiesWidth = 250); e.stopPropagation(); }}
+                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
+                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
                 />
                 : (null)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index a24f1eb7a..623e0f58d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -363,8 +363,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 PanelHeight={this.PanelHeight}
                 layerProvider={this.layerProvider}
                 styleProvider={DefaultStyleProvider}
-                docFilters={CollectionDockingView.Instance.docFilters}
-                docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
+                docFilters={CollectionDockingView.Instance.childDocFilters}
+                docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 addDocument={undefined}
                 removeDocument={undefined}
@@ -468,6 +468,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
                 <CollectionFreeFormView
                     Document={this.props.document}
+                    SetSubView={() => this}
                     CollectionView={undefined}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
@@ -496,8 +497,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     layerProvider={undefined}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
-                    docFilters={CollectionDockingView.Instance.docFilters}
-                    docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
+                    docFilters={CollectionDockingView.Instance.childDocFilters}
+                    docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                     fitContentsToDoc={returnTrue}
                 />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ecc93285e..ba6222605 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -110,8 +110,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
     @observable _keyframeEditing = false;
-    @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
-    @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
@@ -159,8 +157,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.layoutDoc._viewScale = vals.scale;
     }
     freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
-    freeformDocFilters = () => this._focusFilters;
-    freeformRangeDocFilters = () => this._focusRangeFilters;
     reverseNativeScaling = () => this.fitToContent ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
@@ -1030,8 +1026,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ScreenToLocalTransform={childLayout.z ? this.getTransformOverlay : this.getTransform}
             PanelWidth={childLayout[WidthSym]}
             PanelHeight={childLayout[HeightSym]}
-            docFilters={this.freeformDocFilters}
-            docRangeFilters={this.freeformRangeDocFilters}
+            docFilters={this.childDocFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse}
             isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index f3a39a262..65c345547 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -237,8 +237,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            docFilters={this.docFilters}
-            docRangeFilters={this.docRangeFilters}
+            docFilters={this.childDocFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 29cb3511a..30836854a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -236,9 +236,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            docFilters={this.docFilters}
+            docFilters={this.childDocFilters}
             isContentActive={returnFalse}
-            docRangeFilters={this.docRangeFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 585cda729..fed64b620 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -413,8 +413,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     isContentActive={returnTrue}
                     isDocumentActive={returnFalse}
                     ScreenToLocalTransform={this.getPreviewTransform}
-                    docFilters={this.docFilters}
-                    docRangeFilters={this.docRangeFilters}
+                    docFilters={this.childDocFilters}
+                    docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
                     layerProvider={undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c218b805e..bea219831 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -105,9 +105,9 @@ export interface DocumentViewSharedProps {
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: () => boolean;
-    docFilters?: () => Opt<string[]>;
-    docRangeFilters?: () => Opt<string[]>;
-    searchFilterDocs?: () => Opt<Doc[]>;
+    docFilters: () => string[];
+    docRangeFilters: () => string[];
+    searchFilterDocs: () => Doc[];
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
-- 
cgit v1.2.3-70-g09d2


From b25921a0cbdb431726e7089fabdd08fe43c6b3de Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 Aug 2021 09:52:04 -0400
Subject: fixed webbox history to allow duplicate urls

---
 src/client/views/collections/TabDocView.tsx | 1 -
 src/client/views/nodes/DocumentView.tsx     | 2 +-
 src/client/views/nodes/WebBox.tsx           | 8 ++++----
 3 files changed, 5 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 623e0f58d..1f73793b6 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -171,7 +171,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             //attach the selection doc buttons menu to the drag handle
             const stack: HTMLDivElement = tab.contentItem.parent;
             const header: HTMLDivElement = tab;
-            console.log("Stack: " + stack.id, stack.className)
             stack.onscroll = action((e: any) => {
                 console.log('scrolling...')
             })
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bea219831..745d58656 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -420,7 +420,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
         LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
-        // copying over _VIEW fields immediately allows the view type to switch to create the right _componentView
+        // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
         Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
             this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
         });
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 4498a7bf1..751d63711 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        if (StrCast(doc.data)) this.submitURL(StrCast(doc.data));
+        if (StrCast(doc.data) !== this._url) this.submitURL(StrCast(doc.data));
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
         }
@@ -303,7 +303,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (future.length) {
-            history.push(this._url);
+            this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
             this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
             this._urlHash = WebBox.urlHash(this._url) + "";
             this._annotationKey = this._urlHash + "-annotations";
@@ -318,7 +318,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (history.length) {
             if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
-            else future.push(this._url);
+            else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
             this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
             this._urlHash = WebBox.urlHash(this._url) + "";
             this._annotationKey = this._urlHash + "-annotations";
@@ -343,7 +343,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (history === undefined) {
                     this.dataDoc[this.fieldKey + "-history"] = new List<string>([url]);
                 } else {
-                    history.push(url);
+                    this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, url]);
                 }
                 this.layoutDoc._scrollTop = 0;
                 future && (future.length = 0);
-- 
cgit v1.2.3-70-g09d2


From 4d21696c1b49c2c383adcd01974c18461123b83c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Aug 2021 16:55:15 -0400
Subject: made dashboard context menu items context sensitive to whether
 they're called from MyDashboards, MySharedDocs or elsewhere.  Made
 treeViewOpen a playground field so that users don't interfere with each
 other.   Made Docking views not generate alias of their tabs when shared.

---
 src/client/util/CurrentUserUtils.ts                | 35 +++++++++++-----------
 src/client/views/MainView.tsx                      |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 13 ++++++--
 src/client/views/collections/CollectionView.tsx    | 24 +++++++++------
 src/client/views/collections/TreeView.tsx          | 25 +++++++++++-----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 7 files changed, 63 insertions(+), 40 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 34990e121..85762a73f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -765,8 +765,13 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true
             }));
             const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
-            (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
-            (doc.myDashboards as any as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
+            const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
+            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+            const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
+            const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
+            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
+            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
         }
         return doc.myDashboards as any as Doc;
     }
@@ -933,6 +938,11 @@ export class CurrentUserUtils {
             }
             if (sharedDocs instanceof Doc) {
                 Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
+                const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
+                const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
+                sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
+                sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
+                sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
             }
             doc.mySharedDocs = new PrefetchProxy(sharedDocs);
         }
@@ -1228,15 +1238,6 @@ export class CurrentUserUtils {
 
         Doc.AddDocToList(myPresentations, "data", presentation);
         userDoc.activePresentation = presentation;
-        const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
-        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
-        const createDashboard = ScriptField.MakeScript(`createNewDashboard()`);
-        const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
-        const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
-        const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!, shareDashboard!, addToDashboards!, removeDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard", "Share Dashboard", "Add to Dashboards", "Remove Dashboard"]);
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
         CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
@@ -1306,17 +1307,15 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
     const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
 
     // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly
-    dashboard["data-all"] = new List<Doc>(allDocs);
-    dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc)));
-
-    const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig));
-    dockingConfig.content = [];
-    dashboardAlias.dockingConfig = JSON.stringify(dockingConfig);
+    // dashboard["data-all"] = new List<Doc>(allDocs);
+    // dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc)));
 
+    // const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig));
+    // dashboardAlias.dockingConfig = JSON.stringify(dockingConfig);
 
     dashboardAlias.data = new List<Doc>(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder)));
     DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias);
-    DocListCast(dashboardAlias.data)[0].data = new List<Doc>();
+    //new List<Doc>();
     DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
     CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b0b8d7f41..4d2a0a4c4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -107,7 +107,7 @@ export class MainView extends React.Component {
         new InkStrokeProperties();
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
+            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
                 "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
                 "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c0d39b2a2..cae08e1f4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -170,7 +170,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 const aliasDocList = DocListCast(alias["data-all"]);
                 // if aliasDocList contains the alias, don't do anything
                 // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
-                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
             });
         }
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3eece0086..c0553ca60 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,10 +1,10 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, reaction, IReactionDisposer, observable } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
-import { Document } from '../../../fields/Schema';
+import { Document, listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
@@ -215,6 +215,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             />
         </div>;
     }
+    childContextMenuItems = () => {
+        const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
+        const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label }));
+    }
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -247,7 +252,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             this.whenChildContentsActiveChanged,
             this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
             this.observerHeight,
-            this.unobserveHeight);
+            this.unobserveHeight,
+            this.childContextMenuItems()
+        );
     }
     @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e65ebf075..a82128e47 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -70,6 +70,7 @@ export interface CollectionViewProps extends FieldViewProps {
     childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: () => boolean;
     childOpacity?: () => number;
+    childContextMenuItems?: () => { script: ScriptField, label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
     childHideDecorationTitle?: () => boolean;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
@@ -186,15 +187,20 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             }
             !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
 
-            optionItems.push({
-                description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
-            });
-            optionItems.push({
-                description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
-            });
-            optionItems.push({
-                description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
-            });
+            if (!Doc.UserDoc().noviceMode) {
+                optionItems.push({
+                    description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
+                });
+                optionItems.push({
+                    description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
+                });
+                optionItems.push({
+                    description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
+                });
+            }
+            if (this.Document._viewType === CollectionViewType.Docking) {
+                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" });
+            }
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
 
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 206e48aac..0598be49b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
@@ -54,6 +54,7 @@ export interface TreeViewProps {
     indentDocument?: (editTitle: boolean) => void;
     outdentDocument?: (editTitle: boolean) => void;
     ScreenToLocalTransform: () => Transform;
+    contextMenuItems: { script: ScriptField, filter: ScriptField, label: string }[];
     dontRegisterView?: boolean;
     styleProvider?: StyleProviderFunc | undefined;
     treeViewHideHeaderFields: () => boolean;
@@ -336,7 +337,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
                     this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
                     [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                    this.props.dontRegisterView, emptyFunction, emptyFunction);
+                    this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems());
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -418,7 +419,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform,
                         this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
                         [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                        this.props.dontRegisterView, emptyFunction, emptyFunction)}
+                        this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems())}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}>
@@ -507,11 +508,19 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     contextMenuItems = () => {
         const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" };
-        return this.doc.isFolder ? [makeFolder] :
+        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" };
+        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" };
+        return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [makeFolder] :
             Doc.IsSystem(this.doc) ? [] :
                 this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
-                    [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, makeFolder] :
-                    [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }];
+                    [openAlias, makeFolder] :
+                    this.doc.viewType === CollectionViewType.Docking ? [] :
+                        [openAlias, focusDoc])];
+    }
+    childContextMenuItems = () => {
+        const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
+        const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label }));
     }
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
 
@@ -817,7 +826,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         whenChildContentsActiveChanged: (isActive: boolean) => void,
         dontRegisterView: boolean | undefined,
         observerHeight: (ref: any) => void,
-        unobserveHeight: (ref: any) => void
+        unobserveHeight: (ref: any) => void,
+        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[])
     ) {
         const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -882,6 +892,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 parentTreeView={parentTreeView}
                 observeHeight={observerHeight}
                 unobserveHeight={unobserveHeight}
+                contextMenuItems={contextMenuItems}
             />;
         });
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 745d58656..a29f2f662 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -664,7 +664,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
-        Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
+        StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
         this.props.contextMenuItems?.().forEach(item =>
             item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
-- 
cgit v1.2.3-70-g09d2


From 917ebb4a7b6840f57abbf8dbcc197919f3ee3aac Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 18 Aug 2021 17:27:07 -0400
Subject: updates towards a menu

---
 src/client/documents/Documents.ts                  |    1 +
 src/client/util/CurrentUserUtils.ts                |    6 +-
 src/client/views/MainView.scss                     |    5 -
 src/client/views/MainView.tsx                      |    2 +
 src/client/views/StyleProvider.tsx                 |    3 +-
 src/client/views/collections/CollectionMenu.scss   | 1255 ++++++++++----------
 src/client/views/collections/CollectionMenu.tsx    |  101 +-
 .../collectionLinearView/CollectionLinearView.scss |   33 +-
 .../collectionLinearView/CollectionLinearView.tsx  |   43 +-
 src/client/views/nodes/DocumentView.tsx            |   29 +-
 src/client/views/nodes/button/FontIconBadge.tsx    |   13 +-
 src/client/views/nodes/button/FontIconBox.scss     |  262 ++--
 src/client/views/nodes/button/FontIconBox.tsx      |   81 +-
 13 files changed, 1022 insertions(+), 812 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 36cbd2059..817fbb9d6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -227,6 +227,7 @@ export class DocumentOptions {
     //LINEAR VIEW
     linearViewIsExpanded?: boolean; // is linear view expanded
     linearViewExpandable?: boolean; // can linear view be expanded
+    linearViewToggleButton?: string; // button to open close linear view group
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 620602070..cd4c217b5 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -547,7 +547,7 @@ export class CurrentUserUtils {
             { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
-            { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+            // { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
@@ -594,6 +594,7 @@ export class CurrentUserUtils {
                 title: "menuItemPanel",
                 childDropAction: "alias",
                 _chromeHidden: true,
+                backgroundColor: Colors.DARK_GRAY,
                 boxShadow: "rgba(0,0,0,0)",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
                 ignoreClick: true,
@@ -998,7 +999,7 @@ export class CurrentUserUtils {
                         onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
                     }));
                 });
-                docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList));
+                docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, icon:"Text", _height: 30, backgroundColor: "transparent" }, textDocList));
             } else {
                 docList.push(Docs.Create.FontIconDocument({
                     _nativeWidth: width ? width : 30,
@@ -1197,6 +1198,7 @@ export class CurrentUserUtils {
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
+        this.setupContextMenuButtons(doc); // set up context menu buttons
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
         await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index d913f2069..817e45699 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -265,11 +265,6 @@
         height: 35px;
         padding: 5px;
     }
-
-    svg {
-        width: 95% !important;
-        height: 95%;
-    }
 }
 
 .mainView-searchPanel {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6a388c5b4..5c1408148 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -439,6 +439,7 @@ export class MainView extends React.Component {
         this._flyoutWidth = (this._flyoutWidth || 250);
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
+        console.log(button.title);
     });
 
     closeFlyout = action(() => {
@@ -446,6 +447,7 @@ export class MainView extends React.Component {
         this._panelContent = "none";
         this._sidebarContent.proto = undefined;
         this._flyoutWidth = 0;
+        console.log("close flyout");
     });
 
     remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index c9e532745..85520f6b3 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -96,6 +96,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
                 (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
         case StyleProp.Color:
+            if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
             if (docColor) return docColor;
             const backColor = backgroundCol();
@@ -114,8 +115,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) ||
             doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
+            if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
             let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""));
-            if (MainView.Instance.LastButton === doc) return darkScheme() ? Colors.MEDIUM_GRAY : Colors.LIGHT_GRAY;
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
                 case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index f04b19ef7..163566d22 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,628 +1,659 @@
 @import "../global/globalCssVariables";
 
-.collectionMenu-cont {
-    position: relative;
-    display: inline-flex;
-    width: 100%;
-    opacity: 0.9;
-    z-index: 901;
-    transition: top .5s;
-    background: $dark-gray;
-    color: $white;
-    transform-origin: top left;
-    top: 0;
-    width: 100%;
-
-    .recordButtonOutline {
-        border-radius: 100%;
-        width: 18px;
-        height: 18px;
-        border: solid 1px $white;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-    }
-
-    .recordButtonInner {
-        border-radius: 100%;
-        width: 70%;
-        height: 70%;
-        background: $white;
-    }
-
-    .collectionMenu {
-        display: flex;
-        height: 100%;
-        overflow: visible;
-        z-index: 901;
-        border: unset;
-
-        .collectionMenu-divider {
-            height: 100%;
-            margin-left: 3px;
-            margin-right: 3px;
-            width: 2px;
-            background-color: $medium-gray;
-        }
-
-        .collectionViewBaseChrome {
-            display: flex;
-            align-items: center;
-
-            .collectionViewBaseChrome-viewPicker {
-                font-size: $small-text;
-                outline-color: $black;
-                color: $white;
-                border: none;
-                background: $dark-gray;               
-            }
-
-            .collectionViewBaseChrome-viewPicker:focus {
-                outline: none;
-                border: none;
-            }
-
-            .collectionViewBaseChrome-viewPicker:active {
-                outline-color: $black;
-            }
-
-            .collectionViewBaseChrome-button {
-                font-size: $small-text;
-                text-transform: uppercase;
-                letter-spacing: 2px;
-                background: $white;
-                color: $pink;
-                outline-color: $black;
-                border: none;
-                padding: 12px 10px 11px 10px;
-                margin-left: 10px;
-            }
-
-            .collectionViewBaseChrome-cmdPicker {
-                margin-left: 3px;
-                margin-right: 0px;
-                font-size: $small-text;
-                text-transform: capitalize;
-                color: $white;
-                border: none;
-                background: $dark-gray;     
-            }
-
-            .collectionViewBaseChrome-cmdPicker:focus {
-                border: none;
-                outline: none;
-            }
-
-            .commandEntry-outerDiv {
-                pointer-events: all;
-                background-color: transparent;
-                display: flex;
-                flex-direction: row;
-                align-items: center;
-                justify-content: center;
-                height: 100%;
-                overflow: hidden;
-
-                .commandEntry-drop {
-                    color: $white;
-                    width: 30px;
-                    margin-top: auto;
-                    margin-bottom: auto;
-                }
-            }
-
-            .commandEntry-outerDiv:hover{
-                background-color: $drop-shadow;
-
-                .collectionViewBaseChrome-viewPicker,
-                .collectionViewBaseChrome-cmdPicker{
-                    background: $dark-gray;
-                }
-            }
-
-            .collectionViewBaseChrome-collapse {
-                transition: all .5s, opacity 0.3s;
-                position: absolute;
-                width: 30px;
-                transform-origin: top left;
-                pointer-events: all;
-                // margin-top: 10px;
-            }
-
-            @media only screen and (max-device-width: 480px) {
-                .collectionViewBaseChrome-collapse {
-                    display: none;
-                }
-            }
-
-            .collectionViewBaseChrome-template,
-            .collectionViewBaseChrome-viewModes {
-                align-items: center;
-                height: 100%;
-                display: flex;
-                background: transparent;
-                color: $medium-gray;
-                justify-content: center;
-            }
-
-            .collectionViewBaseChrome-viewSpecs {
-                margin-left: 5px;
-                display: grid;
-                border: none;
-                border-right: solid $medium-gray 1px;
-
-                .collectionViewBaseChrome-filterIcon {
-                    position: relative;
-                    display: flex;
-                    margin: auto;
-                    background: $dark-gray;
-                    color: $white;
-                    width: 30px;
-                    height: 30px;
-                    align-items: center;
-                    justify-content: center;
-                    border: none;
-                    border-right: solid $medium-gray 1px;
-                }
-
-                .collectionViewBaseChrome-viewSpecsInput {
-                    padding: 12px 10px 11px 10px;
-                    border: 0px;
-                    color: $medium-gray;
-                    text-align: center;
-                    letter-spacing: 2px;
-                    outline-color: $black;
-                    font-size: $small-text;
-                    background: $white;
-                    height: 100%;
-                    width: 75px;
-                }
-
-                .collectionViewBaseChrome-viewSpecsMenu {
-                    overflow: hidden;
-                    transition: height .5s, display .5s;
-                    position: absolute;
-                    top: 60px;
-                    z-index: 100;
-                    display: flex;
-                    flex-direction: column;
-                    background: $white;
-                    box-shadow: $medium-gray 2px 2px 4px;
-
-                    .qs-datepicker {
-                        left: unset;
-                        right: 0;
-                    }
-
-                    .collectionViewBaseChrome-viewSpecsMenu-row {
-                        display: grid;
-                        grid-template-columns: 150px 200px 150px;
-                        margin-top: 10px;
-                        margin-right: 10px;
-
-                        .collectionViewBaseChrome-viewSpecsMenu-rowLeft,
-                        .collectionViewBaseChrome-viewSpecsMenu-rowMiddle,
-                        .collectionViewBaseChrome-viewSpecsMenu-rowRight {
-                            font-size: $small-text;
-                            letter-spacing: 2px;
-                            color: $medium-gray;
-                            margin-left: 10px;
-                            padding: 5px;
-                            border: none;
-                            outline-color: $black;
-                        }
-                    }
-
-                    .collectionViewBaseChrome-viewSpecsMenu-lastRow {
-                        display: grid;
-                        grid-template-columns: 1fr 1fr 1fr;
-                        grid-gap: 10px;
-                        margin: 10px;
-                    }
-                }
-            }
-        }
-
-        .collectionStackingViewChrome-cont,
-        .collectionTreeViewChrome-cont,
-        .collection3DCarouselViewChrome-cont {
-            display: flex;
-            justify-content: space-between;
-        }
-
-        .collectionGridViewChrome-cont {
-            display: flex;
-            margin-left: 10;
-
-            .collectionGridViewChrome-viewPicker {
-                font-size: $small-text;
-                //text-transform: uppercase;
-                //letter-spacing: 2px;
-                background: $dark-gray;
-                color: $white;
-                outline-color: $black;
-                color: $white;
-                border: none;
-                border-right: solid $medium-gray 1px;
-            }
-
-            .collectionGridViewChrome-viewPicker:active {
-                outline-color: $black;
-            }
-
-            .grid-control {
-                align-self: center;
-                display: flex;
-                flex-direction: row;
-                margin-right: 5px;
-
-                .grid-icon {
-                    margin-right: 5px;
-                    align-self: center;
-                }
-
-                .flexLabel {
-                    margin-bottom: 0;
-                }
-
-                .collectionGridViewChrome-entryBox {
-                    width: 50%;
-                    color: $black;
-                }
-
-                .collectionGridViewChrome-columnButton {
-                    color: $black;
-                }
-            }
-        }
-
-        .collectionStackingViewChrome-sort,
-        .collectionTreeViewChrome-sort {
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-
-            .collectionStackingViewChrome-sortIcon,
-            .collectionTreeViewChrome-sortIcon {
-                transition: transform .5s;
-                margin-left: 10px;
-            }
-        }
-
-        button:hover {
-            transform: scale(1);
-        }
-
-
-        .collectionStackingViewChrome-pivotField-cont,
-        .collectionTreeViewChrome-pivotField-cont,
-        .collection3DCarouselViewChrome-scrollSpeed-cont {
-            justify-self: right;
-            align-items: center;
-            display: flex;
-            grid-auto-columns: auto;
-            font-size: $small-text;
-            letter-spacing: 2px;
-
-            .collectionStackingViewChrome-pivotField-label,
-            .collectionTreeViewChrome-pivotField-label,
-            .collection3DCarouselViewChrome-scrollSpeed-label {
-                grid-column: 1;
-                margin-right: 7px;
-                user-select: none;
-                font-family: $sans-serif;
-                letter-spacing: normal;
-            }
-
-            .collectionStackingViewChrome-sortIcon {
-                transition: transform .5s;
-                grid-column: 3;
-                text-align: center;
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                cursor: pointer;
-                width: 25px;
-                height: 25px;
-                border-radius: 100%;
-            }
-
-            .collectionStackingViewChrome-sortIcon:hover {
-                background-color: $drop-shadow;
-            }
-
-            .collectionStackingViewChrome-pivotField,
-            .collectionTreeViewChrome-pivotField,
-            .collection3DCarouselViewChrome-scrollSpeed {
-                color: $white;
-                grid-column: 2;
-                grid-row: 1;
-                width: 90%;
-                min-width: 100px;
-                display: flex;
-                height: 80%;
-                border-radius: 7px;
-                align-items: center;
-                background: $white;
-
-                .editable-view-input,
-                input,
-                .editableView-container-editing-oneLine,
-                .editableView-container-editing {
-                    margin: auto;
-                    border: 0px;
-                    color: $light-gray !important;
-                    text-align: center;
-                    letter-spacing: 2px;
-                    outline-color: $black;
-                    height: 100%;
-                }
-
-                .react-autosuggest__container {
-                    margin: 0;
-                    color: $medium-gray;
-                    padding: 0px;
-                }
-            }
-        }
-
-        .collectionStackingViewChrome-pivotField:hover,
-        .collectionTreeViewChrome-pivotField:hover,
-        .collection3DCarouselViewChrome-scrollSpeed:hover {
-            cursor: text;
-        }
-
-    }
-}
-
-.collectionMenu-webUrlButtons {
-    margin-left: 44;
-    background: lightGray;
+.collectionMenu-container {
     display: flex;
-}
-
-.webBox-urlEditor {
-    position: relative;
-    opacity: 0.9;
-    z-index: 901;
-    transition: top .5s;
-
-    .urlEditor {
-        display: grid;
-        grid-template-columns: 1fr auto;
-        padding-bottom: 10px;
-        overflow: hidden;
-        margin-top: 5px;
-        height: 35px;
-
-        .editorBase {
-            display: flex;
-
-            .editor-collapse {
-                transition: all .5s, opacity 0.3s;
-                position: absolute;
-                width: 40px;
-                transform-origin: top left;
-            }
-
-            .switchToText {
-                color: $medium-gray;
-            }
-
-            .switchToText:hover {
-                color: $dark-gray;
-            }
-        }
-
-        button:hover {
-            transform: scale(1);
-        }
-    }
-}
-
-.collectionMenu-urlInput {
-    padding: 12px 10px 11px 10px;
-    border: 0px;
-    color: $black;
-    font-size: $small-text;
-    letter-spacing: 2px;
-    outline-color: $black;
-    background: $white;
-    width: 100%;
-    min-width: 350px;
-    margin-right: 10px;
-    height: 100%;
-}
-
-.collectionFreeFormMenu-cont {
-    display: inline-flex;
     position: relative;
+    align-content: center;
+    justify-content: space-between;
+    background-color: $dark-gray;
+    height: 35px;
+    border-bottom: $standard-border;
+    padding-right: 5px;
     align-items: center;
-    height: 100%;
-
-    .color-previewI {
-        width: 60%;
-        top: 80%;
-        position: absolute;
-        height: 4px;
-    }
-
-    .color-previewII {
-        width: 80%;
-        height: 80%;
-        margin-left: 10%;
-        position: absolute;
-        bottom: 5;
-    }
-
-    .btn-group {
-        display: grid;
-        grid-template-columns: auto auto auto auto;
-        margin: auto;
-        /* Make the buttons appear below each other */
-    }
 
-    .btn-draw {
-        display: inline-flex;
-        margin: auto;
-        /* Make the buttons appear below each other */
-    }
-
-    .fwdKeyframe,
-    .numKeyframe,
-    .backKeyframe {
+    .collectionMenu-hardCodedButton {
         cursor: pointer;
-        position: relative;
-        width: 20;
-        height: 30;
-        bottom: 0;
-        background: $dark-gray;
-        display: inline-flex;
-        align-items: center;
         color: $white;
-    }
-
-    .backKeyframe {
-        svg {
-            display: block;
-            margin: auto;
-        }
-    }
-
-
-    .numKeyframe {
-        flex-direction: column;
-        padding-top: 5px;
-    }
-
-    .fwdKeyframe {
-        svg {
-            display: block;
-            margin: auto;
-        }
-
-        border-right: solid $medium-gray 1px;
-    }
-}
-
-.collectionSchemaViewChrome-cont {
-    display: flex;
-    font-size: $small-text;
-
-    .collectionSchemaViewChrome-toggle {
-        display: flex;
-        margin-left: 10px;
-    }
-
-    .collectionSchemaViewChrome-label {
-        text-transform: uppercase;
-        letter-spacing: 2px;
-        margin-right: 5px;
+        width: 31.5px;
+        height: 90%;
+        padding: 5;
+        text-align: center;
         display: flex;
-        flex-direction: column;
         justify-content: center;
-    }
-
-    .collectionSchemaViewChrome-toggler {
-        width: 100px;
-        height: 35px;
-        background-color: $black;
+        align-items: center;
         position: relative;
-    }
-
-    .collectionSchemaViewChrome-togglerButton {
-        width: 47px;
-        height: 30px;
-        background-color: $light-gray;
-        // position: absolute;
-        transition: all 0.5s ease;
-        // top: 3px;
-        margin-top: 3px;
-        color: $medium-gray;
-        letter-spacing: 2px;
-        text-transform: uppercase;
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        text-align: center;
+        transition: 0.2s;
 
-        &.on {
-            margin-left: 3px;
-        }
-
-        &.off {
-            margin-left: 50px;
+        &:hover {
+            border-radius:5px;
+            background-color: rgba(0,0,0,0.2);
         }
     }
 }
 
-
-.commandEntry-outerDiv {
-    display: flex;
-    flex-direction: column;
-    height: 40px;
-}
-
-.commandEntry-inputArea {
-    display: flex;
-    flex-direction: row;
-    width: 150px;
-    margin: auto auto auto auto;
-}
-
-.react-autosuggest__container {
-    position: relative;
-    width: 100%;
-    margin-left: 5px;
-    margin-right: 5px;
-}
-
-.react-autosuggest__input {
-    border: 1px solid $light-gray;
-    border-radius: 4px;
-    width: 100%;
-}
-
-.react-autosuggest__input--focused {
-    outline: none;
-}
-
-.react-autosuggest__input--open {
-    border-bottom-left-radius: 0;
-    border-bottom-right-radius: 0;
-}
-
-.react-autosuggest__suggestions-container {
-    display: none;
-}
-
-.react-autosuggest__suggestions-container--open {
-    display: block;
-    position: fixed;
-    overflow-y: auto;
-    max-height: 400px;
-    width: 180px;
-    border: 1px solid $light-gray;
-    background-color: $white;
-    font-family: $sans-serif;
-    font-weight: 300;
-    font-size: $large-header;
-    border-bottom-left-radius: 4px;
-    border-bottom-right-radius: 4px;
-    z-index: 2;
-}
-
-.react-autosuggest__suggestions-list {
-    margin: 0;
-    padding: 0;
-    list-style-type: none;
-}
-
-.react-autosuggest__suggestion {
-    cursor: pointer;
-    padding: 10px 20px;
-}
-
-.react-autosuggest__suggestion--highlighted {
-    background-color: $light-gray;
-}
\ No newline at end of file
+// .collectionMenu-cont {
+//     position: relative;
+//     display: inline-flex;
+//     width: 100%;
+//     opacity: 0.9;
+//     z-index: 901;
+//     transition: top .5s;
+//     background: $dark-gray;
+//     color: $white;
+//     transform-origin: top left;
+//     top: 0;
+//     width: 100%;
+
+//     .recordButtonOutline {
+//         border-radius: 100%;
+//         width: 18px;
+//         height: 18px;
+//         border: solid 1px $white;
+//         display: flex;
+//         align-items: center;
+//         justify-content: center;
+//     }
+
+//     .recordButtonInner {
+//         border-radius: 100%;
+//         width: 70%;
+//         height: 70%;
+//         background: $white;
+//     }
+
+//     .collectionMenu {
+//         display: flex;
+//         height: 100%;
+//         overflow: visible;
+//         z-index: 901;
+//         border: unset;
+
+//         .collectionMenu-divider {
+//             height: 100%;
+//             margin-left: 3px;
+//             margin-right: 3px;
+//             width: 2px;
+//             background-color: $medium-gray;
+//         }
+
+//         .collectionViewBaseChrome {
+//             display: flex;
+//             align-items: center;
+
+//             .collectionViewBaseChrome-viewPicker {
+//                 font-size: $small-text;
+//                 outline-color: $black;
+//                 color: $white;
+//                 border: none;
+//                 background: $dark-gray;               
+//             }
+
+//             .collectionViewBaseChrome-viewPicker:focus {
+//                 outline: none;
+//                 border: none;
+//             }
+
+//             .collectionViewBaseChrome-viewPicker:active {
+//                 outline-color: $black;
+//             }
+
+//             .collectionViewBaseChrome-button {
+//                 font-size: $small-text;
+//                 text-transform: uppercase;
+//                 letter-spacing: 2px;
+//                 background: $white;
+//                 color: $pink;
+//                 outline-color: $black;
+//                 border: none;
+//                 padding: 12px 10px 11px 10px;
+//                 margin-left: 10px;
+//             }
+
+//             .collectionViewBaseChrome-cmdPicker {
+//                 margin-left: 3px;
+//                 margin-right: 0px;
+//                 font-size: $small-text;
+//                 text-transform: capitalize;
+//                 color: $white;
+//                 border: none;
+//                 background: $dark-gray;     
+//             }
+
+//             .collectionViewBaseChrome-cmdPicker:focus {
+//                 border: none;
+//                 outline: none;
+//             }
+
+//             .commandEntry-outerDiv {
+//                 pointer-events: all;
+//                 background-color: transparent;
+//                 display: flex;
+//                 flex-direction: row;
+//                 align-items: center;
+//                 justify-content: center;
+//                 height: 100%;
+//                 overflow: hidden;
+
+//                 .commandEntry-drop {
+//                     color: $white;
+//                     width: 30px;
+//                     margin-top: auto;
+//                     margin-bottom: auto;
+//                 }
+//             }
+
+//             .commandEntry-outerDiv:hover{
+//                 background-color: $drop-shadow;
+
+//                 .collectionViewBaseChrome-viewPicker,
+//                 .collectionViewBaseChrome-cmdPicker{
+//                     background: $dark-gray;
+//                 }
+//             }
+
+//             .collectionViewBaseChrome-collapse {
+//                 transition: all .5s, opacity 0.3s;
+//                 position: absolute;
+//                 width: 30px;
+//                 transform-origin: top left;
+//                 pointer-events: all;
+//                 // margin-top: 10px;
+//             }
+
+//             @media only screen and (max-device-width: 480px) {
+//                 .collectionViewBaseChrome-collapse {
+//                     display: none;
+//                 }
+//             }
+
+//             .collectionViewBaseChrome-template,
+//             .collectionViewBaseChrome-viewModes {
+//                 align-items: center;
+//                 height: 100%;
+//                 display: flex;
+//                 background: transparent;
+//                 color: $medium-gray;
+//                 justify-content: center;
+//             }
+
+//             .collectionViewBaseChrome-viewSpecs {
+//                 margin-left: 5px;
+//                 display: grid;
+//                 border: none;
+//                 border-right: solid $medium-gray 1px;
+
+//                 .collectionViewBaseChrome-filterIcon {
+//                     position: relative;
+//                     display: flex;
+//                     margin: auto;
+//                     background: $dark-gray;
+//                     color: $white;
+//                     width: 30px;
+//                     height: 30px;
+//                     align-items: center;
+//                     justify-content: center;
+//                     border: none;
+//                     border-right: solid $medium-gray 1px;
+//                 }
+
+//                 .collectionViewBaseChrome-viewSpecsInput {
+//                     padding: 12px 10px 11px 10px;
+//                     border: 0px;
+//                     color: $medium-gray;
+//                     text-align: center;
+//                     letter-spacing: 2px;
+//                     outline-color: $black;
+//                     font-size: $small-text;
+//                     background: $white;
+//                     height: 100%;
+//                     width: 75px;
+//                 }
+
+//                 .collectionViewBaseChrome-viewSpecsMenu {
+//                     overflow: hidden;
+//                     transition: height .5s, display .5s;
+//                     position: absolute;
+//                     top: 60px;
+//                     z-index: 100;
+//                     display: flex;
+//                     flex-direction: column;
+//                     background: $white;
+//                     box-shadow: $medium-gray 2px 2px 4px;
+
+//                     .qs-datepicker {
+//                         left: unset;
+//                         right: 0;
+//                     }
+
+//                     .collectionViewBaseChrome-viewSpecsMenu-row {
+//                         display: grid;
+//                         grid-template-columns: 150px 200px 150px;
+//                         margin-top: 10px;
+//                         margin-right: 10px;
+
+//                         .collectionViewBaseChrome-viewSpecsMenu-rowLeft,
+//                         .collectionViewBaseChrome-viewSpecsMenu-rowMiddle,
+//                         .collectionViewBaseChrome-viewSpecsMenu-rowRight {
+//                             font-size: $small-text;
+//                             letter-spacing: 2px;
+//                             color: $medium-gray;
+//                             margin-left: 10px;
+//                             padding: 5px;
+//                             border: none;
+//                             outline-color: $black;
+//                         }
+//                     }
+
+//                     .collectionViewBaseChrome-viewSpecsMenu-lastRow {
+//                         display: grid;
+//                         grid-template-columns: 1fr 1fr 1fr;
+//                         grid-gap: 10px;
+//                         margin: 10px;
+//                     }
+//                 }
+//             }
+//         }
+
+//         .collectionStackingViewChrome-cont,
+//         .collectionTreeViewChrome-cont,
+//         .collection3DCarouselViewChrome-cont {
+//             display: flex;
+//             justify-content: space-between;
+//         }
+
+//         .collectionGridViewChrome-cont {
+//             display: flex;
+//             margin-left: 10;
+
+//             .collectionGridViewChrome-viewPicker {
+//                 font-size: $small-text;
+//                 //text-transform: uppercase;
+//                 //letter-spacing: 2px;
+//                 background: $dark-gray;
+//                 color: $white;
+//                 outline-color: $black;
+//                 color: $white;
+//                 border: none;
+//                 border-right: solid $medium-gray 1px;
+//             }
+
+//             .collectionGridViewChrome-viewPicker:active {
+//                 outline-color: $black;
+//             }
+
+//             .grid-control {
+//                 align-self: center;
+//                 display: flex;
+//                 flex-direction: row;
+//                 margin-right: 5px;
+
+//                 .grid-icon {
+//                     margin-right: 5px;
+//                     align-self: center;
+//                 }
+
+//                 .flexLabel {
+//                     margin-bottom: 0;
+//                 }
+
+//                 .collectionGridViewChrome-entryBox {
+//                     width: 50%;
+//                     color: $black;
+//                 }
+
+//                 .collectionGridViewChrome-columnButton {
+//                     color: $black;
+//                 }
+//             }
+//         }
+
+//         .collectionStackingViewChrome-sort,
+//         .collectionTreeViewChrome-sort {
+//             display: flex;
+//             align-items: center;
+//             justify-content: space-between;
+
+//             .collectionStackingViewChrome-sortIcon,
+//             .collectionTreeViewChrome-sortIcon {
+//                 transition: transform .5s;
+//                 margin-left: 10px;
+//             }
+//         }
+
+//         button:hover {
+//             transform: scale(1);
+//         }
+
+
+//         .collectionStackingViewChrome-pivotField-cont,
+//         .collectionTreeViewChrome-pivotField-cont,
+//         .collection3DCarouselViewChrome-scrollSpeed-cont {
+//             justify-self: right;
+//             align-items: center;
+//             display: flex;
+//             grid-auto-columns: auto;
+//             font-size: $small-text;
+//             letter-spacing: 2px;
+
+//             .collectionStackingViewChrome-pivotField-label,
+//             .collectionTreeViewChrome-pivotField-label,
+//             .collection3DCarouselViewChrome-scrollSpeed-label {
+//                 grid-column: 1;
+//                 margin-right: 7px;
+//                 user-select: none;
+//                 font-family: $sans-serif;
+//                 letter-spacing: normal;
+//             }
+
+//             .collectionStackingViewChrome-sortIcon {
+//                 transition: transform .5s;
+//                 grid-column: 3;
+//                 text-align: center;
+//                 display: flex;
+//                 justify-content: center;
+//                 align-items: center;
+//                 cursor: pointer;
+//                 width: 25px;
+//                 height: 25px;
+//                 border-radius: 100%;
+//             }
+
+//             .collectionStackingViewChrome-sortIcon:hover {
+//                 background-color: $drop-shadow;
+//             }
+
+//             .collectionStackingViewChrome-pivotField,
+//             .collectionTreeViewChrome-pivotField,
+//             .collection3DCarouselViewChrome-scrollSpeed {
+//                 color: $white;
+//                 grid-column: 2;
+//                 grid-row: 1;
+//                 width: 90%;
+//                 min-width: 100px;
+//                 display: flex;
+//                 height: 80%;
+//                 border-radius: 7px;
+//                 align-items: center;
+//                 background: $white;
+
+//                 .editable-view-input,
+//                 input,
+//                 .editableView-container-editing-oneLine,
+//                 .editableView-container-editing {
+//                     margin: auto;
+//                     border: 0px;
+//                     color: $light-gray !important;
+//                     text-align: center;
+//                     letter-spacing: 2px;
+//                     outline-color: $black;
+//                     height: 100%;
+//                 }
+
+//                 .react-autosuggest__container {
+//                     margin: 0;
+//                     color: $medium-gray;
+//                     padding: 0px;
+//                 }
+//             }
+//         }
+
+//         .collectionStackingViewChrome-pivotField:hover,
+//         .collectionTreeViewChrome-pivotField:hover,
+//         .collection3DCarouselViewChrome-scrollSpeed:hover {
+//             cursor: text;
+//         }
+
+//     }
+// }
+
+// .collectionMenu-webUrlButtons {
+//     margin-left: 44;
+//     background: lightGray;
+//     display: flex;
+// }
+
+// .webBox-urlEditor {
+//     position: relative;
+//     opacity: 0.9;
+//     z-index: 901;
+//     transition: top .5s;
+
+//     .urlEditor {
+//         display: grid;
+//         grid-template-columns: 1fr auto;
+//         padding-bottom: 10px;
+//         overflow: hidden;
+//         margin-top: 5px;
+//         height: 35px;
+
+//         .editorBase {
+//             display: flex;
+
+//             .editor-collapse {
+//                 transition: all .5s, opacity 0.3s;
+//                 position: absolute;
+//                 width: 40px;
+//                 transform-origin: top left;
+//             }
+
+//             .switchToText {
+//                 color: $medium-gray;
+//             }
+
+//             .switchToText:hover {
+//                 color: $dark-gray;
+//             }
+//         }
+
+//         button:hover {
+//             transform: scale(1);
+//         }
+//     }
+// }
+
+// .collectionMenu-urlInput {
+//     padding: 12px 10px 11px 10px;
+//     border: 0px;
+//     color: $black;
+//     font-size: $small-text;
+//     letter-spacing: 2px;
+//     outline-color: $black;
+//     background: $white;
+//     width: 100%;
+//     min-width: 350px;
+//     margin-right: 10px;
+//     height: 100%;
+// }
+
+// .collectionFreeFormMenu-cont {
+//     display: inline-flex;
+//     position: relative;
+//     align-items: center;
+//     height: 100%;
+
+//     .color-previewI {
+//         width: 60%;
+//         top: 80%;
+//         position: absolute;
+//         height: 4px;
+//     }
+
+//     .color-previewII {
+//         width: 80%;
+//         height: 80%;
+//         margin-left: 10%;
+//         position: absolute;
+//         bottom: 5;
+//     }
+
+//     .btn-group {
+//         display: grid;
+//         grid-template-columns: auto auto auto auto;
+//         margin: auto;
+//         /* Make the buttons appear below each other */
+//     }
+
+//     .btn-draw {
+//         display: inline-flex;
+//         margin: auto;
+//         /* Make the buttons appear below each other */
+//     }
+
+//     .fwdKeyframe,
+//     .numKeyframe,
+//     .backKeyframe {
+//         cursor: pointer;
+//         position: relative;
+//         width: 20;
+//         height: 30;
+//         bottom: 0;
+//         background: $dark-gray;
+//         display: inline-flex;
+//         align-items: center;
+//         color: $white;
+//     }
+
+//     .backKeyframe {
+//         svg {
+//             display: block;
+//             margin: auto;
+//         }
+//     }
+
+
+//     .numKeyframe {
+//         flex-direction: column;
+//         padding-top: 5px;
+//     }
+
+//     .fwdKeyframe {
+//         svg {
+//             display: block;
+//             margin: auto;
+//         }
+
+//         border-right: solid $medium-gray 1px;
+//     }
+// }
+
+// .collectionSchemaViewChrome-cont {
+//     display: flex;
+//     font-size: $small-text;
+
+//     .collectionSchemaViewChrome-toggle {
+//         display: flex;
+//         margin-left: 10px;
+//     }
+
+//     .collectionSchemaViewChrome-label {
+//         text-transform: uppercase;
+//         letter-spacing: 2px;
+//         margin-right: 5px;
+//         display: flex;
+//         flex-direction: column;
+//         justify-content: center;
+//     }
+
+//     .collectionSchemaViewChrome-toggler {
+//         width: 100px;
+//         height: 35px;
+//         background-color: $black;
+//         position: relative;
+//     }
+
+//     .collectionSchemaViewChrome-togglerButton {
+//         width: 47px;
+//         height: 30px;
+//         background-color: $light-gray;
+//         // position: absolute;
+//         transition: all 0.5s ease;
+//         // top: 3px;
+//         margin-top: 3px;
+//         color: $medium-gray;
+//         letter-spacing: 2px;
+//         text-transform: uppercase;
+//         display: flex;
+//         flex-direction: column;
+//         justify-content: center;
+//         text-align: center;
+
+//         &.on {
+//             margin-left: 3px;
+//         }
+
+//         &.off {
+//             margin-left: 50px;
+//         }
+//     }
+// }
+
+
+// .commandEntry-outerDiv {
+//     display: flex;
+//     flex-direction: column;
+//     height: 40px;
+// }
+
+// .commandEntry-inputArea {
+//     display: flex;
+//     flex-direction: row;
+//     width: 150px;
+//     margin: auto auto auto auto;
+// }
+
+// .react-autosuggest__container {
+//     position: relative;
+//     width: 100%;
+//     margin-left: 5px;
+//     margin-right: 5px;
+// }
+
+// .react-autosuggest__input {
+//     border: 1px solid $light-gray;
+//     border-radius: 4px;
+//     width: 100%;
+// }
+
+// .react-autosuggest__input--focused {
+//     outline: none;
+// }
+
+// .react-autosuggest__input--open {
+//     border-bottom-left-radius: 0;
+//     border-bottom-right-radius: 0;
+// }
+
+// .react-autosuggest__suggestions-container {
+//     display: none;
+// }
+
+// .react-autosuggest__suggestions-container--open {
+//     display: block;
+//     position: fixed;
+//     overflow-y: auto;
+//     max-height: 400px;
+//     width: 180px;
+//     border: 1px solid $light-gray;
+//     background-color: $white;
+//     font-family: $sans-serif;
+//     font-weight: 300;
+//     font-size: $large-header;
+//     border-bottom-left-radius: 4px;
+//     border-bottom-right-radius: 4px;
+//     z-index: 2;
+// }
+
+// .react-autosuggest__suggestions-list {
+//     margin: 0;
+//     padding: 0;
+//     list-style-type: none;
+// }
+
+// .react-autosuggest__suggestion {
+//     cursor: pointer;
+//     padding: 10px 20px;
+// }
+
+// .react-autosuggest__suggestion--highlighted {
+//     background-color: $light-gray;
+// }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8f4df4a92..55af4650f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -15,29 +15,31 @@ import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DragManager } from "../../util/DragManager";
 import { Scripting } from "../../util/Scripting";
 import { SelectionManager } from "../../util/SelectionManager";
+import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
 import { EditableView } from "../EditableView";
 import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke";
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
+import { LightboxView } from "../LightboxView";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocumentView } from "../nodes/DocumentView";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
 import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
 import { PresBox } from "../nodes/trails/PresBox";
+import { DefaultStyleProvider } from "../StyleProvider";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionLinearView } from "./CollectionLinearView";
 import "./CollectionMenu.scss";
 import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { TabDocView } from "./TabDocView";
-import { LightboxView } from "../LightboxView";
-import { Docs } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
 
 @observer
 export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -46,6 +48,8 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable SelectedCollection: DocumentView | undefined;
     @observable FieldKey: string;
 
+    private _docBtnRef = React.createRef<HTMLDivElement>();
+
     constructor(props: any) {
         super(props);
         this.FieldKey = "";
@@ -82,30 +86,85 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
         }
     }
 
+    buttonBarXf = () => {
+        if (!this._docBtnRef.current) return Transform.Identity();
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+        return new Transform(-translateX, -translateY, 1 / scale);
+    }
+
+    @computed get contMenuButtons() {
+        const selDoc = Doc.UserDoc().contextMenuBtns;
+        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: "35px" }} >
+            <CollectionLinearView
+                Document={selDoc}
+                DataDoc={undefined}
+                fieldKey={"data"}
+                dropAction={"alias"}
+                setHeight={returnFalse}
+                styleProvider={DefaultStyleProvider}
+                layerProvider={undefined}
+                rootSelected={returnTrue}
+                bringToFront={emptyFunction}
+                select={emptyFunction}
+                isContentActive={returnFalse}
+                isSelected={returnFalse}
+                docViewPath={returnEmptyDoclist}
+                moveDocument={returnFalse}
+                CollectionView={undefined}
+                addDocument={returnFalse}
+                addDocTab={returnFalse}
+                pinToPres={emptyFunction}
+                removeDocument={returnFalse}
+                ScreenToLocalTransform={this.buttonBarXf}
+                PanelWidth={() => 100}
+                PanelHeight={() => 35}
+                renderDepth={0}
+                focus={() => undefined}
+                whenChildContentsActiveChanged={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined} />
+        </div>;
+    }
+
     render() {
-        const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
-            <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
-                <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
-            </button>
-        </Tooltip>;
 
         const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
         const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
 
         const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
-            <button className="antimodeMenu-button" key="properties" style={{ backgroundColor: "#424242" }}
+            <div className="collectionMenu-hardCodedButton" key="properties"
                 onPointerDown={this.toggleProperties}>
                 <FontAwesomeIcon icon={propIcon} size="lg" />
-            </button>
+            </div>
         </Tooltip>;
 
-        return this.getElement(!this.SelectedCollection ? [/*button*/] :
-            [<CollectionViewBaseChrome key="chrome"
-                docView={this.SelectedCollection}
-                fieldKey={this.SelectedCollection.LayoutFieldKey}
-                type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
-                prop,
-                /*button*/]);
+        // NEW BUTTONS
+        //dash col linear view buttons
+        const contMenuButtons = 
+        <div className="collectionMenu-container">
+            {this.contMenuButtons}
+            {prop}
+        </div>;
+
+        return contMenuButtons;
+
+        // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
+        //     <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
+        //         <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
+        //     </button>
+        // </Tooltip>;
+
+        // OLD BUTTONS
+        // return this.getElement(!this.SelectedCollection ? [/*button*/] :
+        //     [<CollectionViewBaseChrome key="chrome"
+        //         docView={this.SelectedCollection}
+        //         fieldKey={this.SelectedCollection.LayoutFieldKey}
+        //         type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
+        //         prop,
+        //         /*button*/]);
     }
 }
 
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
index a10d43917..db39e304b 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
@@ -5,12 +5,22 @@
     overflow: visible;
     height: 100%;
     pointer-events: none;
+    // background-color: rgba(0, 0, 0, 0.2);
+    border-radius: 5px;
+    padding-left: 5px;
+    padding-right: 5px;
+    border-left: $standard-border;
+    border-right: $standard-border;
 
     .collectionLinearView {
         display: flex;
         height: 100%;
         align-items: center;
 
+        .collectionView{
+            overflow: visible !important;
+        }
+
         >span {
             background: $dark-gray;
             color: $white;
@@ -67,29 +77,25 @@
         }
 
         >label {
-            margin-top: "auto";
-            margin-bottom: "auto";
             background: $dark-gray;
             color: $white;
-            display: inline-block;
+            display: flex;
             border-radius: 18px;
             font-size: 12.5px;
-            width: 18px;
-            height: 18px;
+            font-weight:100;
+            width: fit-content;
+            height: 100%;
             margin-top: auto;
             margin-bottom: auto;
             margin-right: 3px;
             cursor: pointer;
             transition: transform 0.2s;
-        }
-
-        label p {
-            padding-left: 5px;
-        }
+            align-items: center;
+            justify-content: center;
 
-        label:hover {
-            background: $medium-gray;
-            transform: scale(1.15);
+            &:hover {
+                background: $medium-gray;
+            }
         }
 
         >input {
@@ -110,7 +116,6 @@
             display: flex;
             opacity: 1;
             position: relative;
-            margin-top: auto;
 
             .collectionLinearView-docBtn,
             .collectionLinearView-docBtn-scalable {
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 8e2ba2275..e7970758a 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -2,20 +2,21 @@ import { Tooltip } from '@material-ui/core';
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { makeInterface } from '../../../../fields/Schema';
+import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
-import { CollectionSubView } from '../CollectionSubView';
+import { Id } from '../../../../fields/FieldSymbols';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
-import { ScriptCast, NumCast, StrCast, BoolCast } from '../../../../fields/Types';
-import { HeightSym, Doc, WidthSym } from '../../../../fields/Doc';
-import { Utils, returnFalse, returnTrue, emptyFunction, returnEmptyDoclist } from '../../../../Utils';
+import { Transform } from '../../../util/Transform';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
 import { StyleProp } from '../../StyleProvider';
-import { CollectionViewType } from '../CollectionView';
-import { Id } from '../../../../fields/FieldSymbols';
-import { DocumentView } from '../../nodes/DocumentView';
-import { Transform } from '../../../util/Transform';
+import "./CollectionLinearView.scss";
+import { CollectionSubView } from '.././CollectionSubView';
+import { CollectionViewType } from '.././CollectionView';
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -108,33 +109,37 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     render() {
         const guid = Utils.GenerateGuid();
         const flexDir: any = StrCast(this.Document.flexDirection);
+        const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const icon: string = StrCast(this.props.Document.icon);
 
         const menuOpener = <label htmlFor={`${guid}`} style={{ pointerEvents: "all", cursor: "pointer", background: backgroundColor === color ? "black" : backgroundColor, }}
             onPointerDown={e => e.stopPropagation()} >
-            <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "–" : "+"}</p>
+            <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : "–" : icon ? icon : "+"}</p>
         </label>;
 
         return <div className="collectionLinearView-outer">
             <div className="collectionLinearView" ref={this.createDashEventsTarget} >
-                <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top">
+                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top">
                     {menuOpener}
-                </Tooltip>
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.layoutDoc.linearViewIsExpanded)} ref={this.addMenuToggle}
-                    onChange={action(() => this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
+                </Tooltip>}
+                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
 
                 <div className="collectionLinearView-content" style={{ height: this.dimension(), flexDirection: flexDir }}>
                     {this.childLayoutPairs.map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
-                        const scalable = pair.layout.onClick || pair.layout.onDragStart;
-                        return <div className={`collectionLinearView-docBtn` + (scalable ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
+                        // const scalable = pair.layout.onClick || pair.layout.onDragStart;
+                        console.log("Height:" + NumCast(pair.layout._height));
+                        return <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
                             style={{
                                 pointerEvents: "all",
-                                minWidth: 30,
-                                width: nested ? pair.layout[WidthSym]() : this.dimension(),
-                                height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension(),
+                                width: nested ? undefined : NumCast(pair.layout._width),
+                                height: nested ? undefined : NumCast(pair.layout._height),
+                                // width: NumCast(pair.layout._width),
+                                // height: NumCast(pair.layout._height),
                             }}  >
                             <DocumentView
                                 Document={pair.layout}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a29f2f662..ae3107b08 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { ScriptingBox } from "./ScriptingBox";
 import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import { ColorScheme } from "../../util/SettingsManager";
 const { Howl } = require('howler');
 
 interface Window {
@@ -132,7 +133,7 @@ export interface DocumentViewSharedProps {
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
     freezeDimensions?: boolean;
-    hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected 
+    hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     treeViewDoc?: Doc;
@@ -420,13 +421,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
         LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
-        // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
-        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
-            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
-        });
+              // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
+              Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
+                this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
+            });        
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here      
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
@@ -764,7 +765,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
-
+        
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
         DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
@@ -949,12 +950,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @computed get renderDoc() {
         TraceMobx();
+        const isButton: boolean = this.props.Document.type === DocumentType.FONTICON;
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
                 style={{
-                    background: this.backgroundColor,
+                    background: isButton ? undefined : this.backgroundColor,
                     opacity: this.opacity,
                     color: StrCast(this.layoutDoc.color, "inherit"),
                     fontFamily: StrCast(this.Document._fontFamily, "inherit"),
@@ -970,7 +972,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     render() {
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ?
+        const highlightColor = (Doc.UserDoc().colorScheme === ColorScheme.Dark ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
@@ -982,6 +984,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
         const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
+
+        // Return surrounding highlight 
         return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
             onKeyDown={this.onKeyDown}
@@ -1142,14 +1146,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         TraceMobx();
         const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+        const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
         return (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth() ? (null) : (
                 <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
                     style={{
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
-                        transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
+                        width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
+                        height: isButton ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index 3be723052..3e451eea6 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -1,11 +1,10 @@
-
-import { AclPrivate, Doc, DocListCast } from '../../../../fields/Doc';
-import { GetEffectiveAcl } from '../../../../fields/util';
+import { observer } from "mobx-react";
+import React from "react";
+import { AclPrivate, Doc, DocListCast } from "../../../../fields/Doc";
+import { GetEffectiveAcl } from "../../../../fields/util";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
-import { DragManager } from '../../../util/DragManager';
-import './FontIconBox.scss';
-import { observer } from 'mobx-react';
-import React from 'react';
+import { DragManager } from "../../../util/DragManager";
+
 interface FontIconBadgeProps {
     collection: Doc | undefined;
 }
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index d4f1b9379..46a499466 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -1,103 +1,205 @@
 @import "../../global/globalCssVariables";
 
-.fontIconBox-label {
-    color: $white;
-    margin-right: 4px;
-    margin-top: 1px;
-    position: relative;
-    text-align: center;
-    font-size: 7px;
-    letter-spacing: normal;
-    background-color: inherit;
-    border-radius: 8px;
-    margin-top: -8px;
-    padding: 0;
-    width: 100%;
-}
+.menuButton {
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 80%;
+    border-radius: 7px;
 
-.fontIconBadge-container {
-    position:absolute;
-    z-index: 1000;
-    top: 12px;
+    .menuButton-wrap {
+        grid-column: 1;
+        justify-content: center;
+        align-items: center;
+        text-align: center;
+    }
 
-    .fontIconBadge {
-        position: absolute;
-        top: -10px;
-        right: -10px;
+    .fontIconBox-label {
         color: $white;
-        background: $pink;
-        font-weight: 300;
-        border-radius: 100%;
-        width: 25px;
-        height: 25px;
+        position: relative;
         text-align: center;
-        padding-top: 4px;
-        font-size: 12px;
+        font-size: 7px;
+        letter-spacing: normal;
+        background-color: inherit;
+        margin-top: 5px;
+        border-radius: 8px;
+        padding: 0;
+        width: 100%;
+        font-family: 'ROBOTO';
+        text-transform: uppercase;
+        font-weight: bold;
     }
-}
 
-.menuButton-circle,
-.menuButton-round {
-    border-radius: 100%;
-    background-color: $dark-gray;
-    padding: 0;
-
-    .fontIconBox-label {
-        //margin-left: -10px; // button padding is 10px;
-        bottom: 0;
-        position: absolute;
+    .fontIconBox-icon {
+        width: 80%;
+        height: 80%;
     }
 
-    &:hover {
-        background-color: $light-gray;
+    &.clickBtn {
+        cursor: pointer;
     }
-}
 
-.menuButton-square {
-    padding-top: 3px;
-    padding-bottom: 3px;
-    background-color: $dark-gray;
+    &.tglBtn {
+        cursor: pointer;
+    }
 
-    .fontIconBox-label {
+    &.menuBtn {
+        cursor: pointer;
         border-radius: 0px;
-        margin-top: 0px;
-        border-radius: "inherit";
+        flex-direction: column;
+
+        svg {
+            width: 45% !important;
+            height: 45%;
+        }
     }
-}
 
-.menuButton,
-.menuButton-circle,
-.menuButton-round,
-.menuButton-square {
-    margin-left: -5%;
-    width: 110%;
-    height: 100%;
-    pointer-events: all;
-    touch-action: none;
+    &.colorBtn {
+        color: black;
+        cursor: pointer;
+        flex-direction: row;
+        background: transparent;
 
-    .menuButton-wrap {
-        touch-action: none;
-        border-radius: 8px;
-        width: 100%;
+        .menuButton-dropdownBox {
+            position: absolute;
+            width: 150px;
+            height: 250px;
+            top: 100%;
+            z-index: 21;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+        }
     }
 
-    .menuButton-icon-square {
-        width: auto;
-        height: 29px;
-        padding: 4px;
+    &.drpdownList {
+        width: 100px;
+        display: grid;
+        grid-auto-columns: 80px 20px;
+        justify-items: flex-start;
+        padding-left: 10px;
+        font-family: 'Roboto';
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        font-size: 13;
+        font-weight: 600;
+        overflow: hidden;
+        cursor: pointer;
+        background: transparent;
+
+        .menuButton-dropdownList {
+            position: absolute;
+            width: 150px;
+            height: fit-content;
+            top: 100%;
+            z-index: 21;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+
+            .list-item {
+                color: black;
+                width: 100%;
+                height: 25px;
+                display: flex;
+                justify-content: left;
+                align-items: center;
+                padding-left: 5px;
+            }
+
+            .list-item:hover {
+                background-color:lightgrey;
+            }
+        }
     }
 
-    svg {
-        width: 95% !important;
-        height: 95%;
+    &.drpDownBtn {
+        cursor: pointer;
+        background: transparent;
+
+        &.true {
+            background: rgba(0, 0, 0, 0.3);
+        }
+
+        .menuButton-dropdownBox {
+            position: absolute;
+            width: 150px;
+            height: 250px;
+            top: 100%;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+        }
     }
+
 }
-.menuButton-round {
-    width: 100%;
-    svg {
-        width: 50% !important;
-        height: 50%;
-        position: relative;
-        bottom: 2px;
-    }
-}
\ No newline at end of file
+
+
+// .fontIconBadge-container {
+//     position: absolute;
+//     z-index: 1000;
+//     top: 12px;
+
+//     .fontIconBadge {
+//         position: absolute;
+//         top: -10px;
+//         right: -10px;
+//         color: $white;
+//         background: $pink;
+//         font-weight: 300;
+//         border-radius: 100%;
+//         width: 25px;
+//         height: 25px;
+//         text-align: center;
+//         padding-top: 4px;
+//         font-size: 12px;
+//     }
+// }
+
+// .menuButton-circle,
+// .menuButton-round {
+//     border-radius: 100%;
+//     background-color: $dark-gray;
+//     padding: 0;
+
+//     .fontIconBox-label {
+//         z-index: 100;
+//         min-width: fit-content;
+//         bottom: 0;
+//         left: 0;
+//         width: 100%;
+//         /* justify-self: center; */
+//         border-radius: 0px;
+//         position: absolute;
+//     }
+
+//     .menuButton-dropdown {
+//         display: flex;
+//         justify-content: center;
+//         align-items: center;
+//         font-size: 15px;
+//         /* background-color: #b9b9b9; */
+//         grid-column: 2;
+//         border-radius: 0px 7px 7px 0px;
+//         /* position: absolute; */
+//         width: 13px;
+//         height: 100%;
+//         right: 0;
+//     }
+
+//     &:hover {
+//         background-color: $light-gray;
+//     }
+
+//     .dropbox-background {
+//         width: 100vw;
+//         height: 100vh;
+//         top: 0;
+//         z-index: 20;
+//         left: 0;
+//         background:transparent;
+//         position: fixed;
+//     }
+// }
+
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f98d01dfc..2c6369e9f 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -2,20 +2,19 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import React from 'react';
+import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, StrListCast } from '../../../../fields/Doc';
 import { createSchema, makeInterface } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, StrCast, ScriptCast } from '../../../../fields/Types';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { ColorScheme } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { StyleProp } from '../../StyleProvider';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { FontIconBadge } from './FontIconBadge';
+import { FieldView, FieldViewProps } from '.././FieldView';
 import './FontIconBox.scss';
 const FontIconSchema = createSchema({
     icon: "string",
@@ -31,10 +30,14 @@ export enum ButtonType {
     ColorButton = "colorBtn"
 }
 
+export interface ButtonProps extends FieldViewProps {
+    type?: ButtonType;
+}
+
 type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
 const FontIconDocument = makeInterface(FontIconSchema);
 @observer
-export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(FontIconDocument) {
+export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
     showTemplate = (): void => {
         const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -56,8 +59,6 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
     // Determining UI Specs
     @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
-    @observable private color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-    @observable private backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
     @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
     @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
     @observable private dropdownDirection: string = StrCast(this.rootDoc.dropDownDirection);
@@ -82,16 +83,18 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
      */
     @computed get dropdownButton() {
         const active: string = StrCast(this.rootDoc.dropDownOpen);
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         return (
             <div className={`menuButton ${this.type} ${active}`}
-                style={{ color: "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}> {this.label} </div>}
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
-                    <FontAwesomeIcon icon={'caret-down'} color={"black"} size="sm" />
+                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
                 </div>
                 {this.rootDoc.dropDownOpen ?
                     <div className="menuButton-dropdownBox"
@@ -121,14 +124,16 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
      * Default
      */
     @computed get defaultButton() {
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const active: string = StrCast(this.rootDoc.dropDownOpen);
         return (
             <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu}
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}> {this.label} </div>}
-                    <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                    {/* <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} /> */}
                 </div>
             </div>
         )
@@ -136,6 +141,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
 
     render() {
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         // Variables called through eval (from button)
         const canUndo: boolean = UndoManager.CanUndo();
         const canRedo: boolean = UndoManager.CanRedo();
@@ -144,7 +151,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         const userDoc = Doc.UserDoc();
 
         // Toggle and canClick properties as determined from the variable passed into the button doc
-        // const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined;
+        const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined;
         const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false;
         // if (toggle) {
         //     console.log(StrCast(this.rootDoc.title), toggle);
@@ -154,11 +161,11 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
         const active: string = StrCast(this.rootDoc.dropDownOpen);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}>
+            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}>
+            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
         const dropdownCaret = <div
@@ -166,7 +173,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
             <FontAwesomeIcon icon={'caret-down'} color={"black"} size="sm" />
         </div>;
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
+        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(backgroundColor)}
             presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
                 '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
                 '#FFFFFF', '#f1efeb', 'transparent']} />;
@@ -207,9 +214,9 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             case ButtonType.DropdownList:
                 button = (
                     <div className={`menuButton ${this.type} ${active}`}
-                        style={{ backgroundColor: this.rootDoc.dropDownOpen ? "#aeddf880" : undefined, color: this.rootDoc.dropDownOpen ? "#1c5c96" : "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        style={{ backgroundColor: this.rootDoc.dropDownOpen ? "#aeddf880" : backgroundColor, color: this.rootDoc.dropDownOpen ? "#1c5c96" : color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                         onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}>
-                        {/* {toggle} */}
+                        {toggle}
                         {label}
                         {dropdownCaret}
                         {this.rootDoc.dropDownOpen ?
@@ -227,10 +234,10 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             case ButtonType.ColorButton:
                 button = (
                     <div className={`menuButton ${this.type} ${active}`}
-                        style={{ opacity: selectedDoc ? 1 : 0.5, color: dark ? "white" : "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        style={{ opacity: selectedDoc ? 1 : 0.5, color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                         onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                         onPointerDown={e => e.stopPropagation()}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={dark ? "white" : "black"} />
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                         {dropdownCaret}
                         {this.rootDoc.dropDownOpen ?
@@ -247,40 +254,36 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
                     </div>
                 );
                 break;
-            // case ButtonType.ToggleButton:
-            //     button = (
-            //         <div className={`menuButton ${this.type}`} style={{ opacity: canClick ? 1 : 0.4, backgroundColor: toggle ? this.backgroundColor : undefined }}>
-            //             <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={toggle ? this.color : this.backgroundColor} />
-            //             {label}
-            //         </div>
-            //     );
-            //     break;
+            case ButtonType.ToggleButton:
+                button = (
+                    <div className={`menuButton ${this.type}`} style={{ opacity: canClick ? 1 : 0.4, backgroundColor: toggle ? backgroundColor : undefined }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={toggle ? color : backgroundColor} />
+                        {label}
+                    </div>
+                );
+                break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: this.color, backgroundColor: this.backgroundColor, opacity: canClick ? 1 : 0.4 }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: canClick ? 1 : 0.4 }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
                 );
                 break;
             case ButtonType.DoubleButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: this.color, backgroundColor: this.backgroundColor }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
                 );
                 break;
             case ButtonType.MenuButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: dark ? "white" : "black", backgroundColor: dark ? "black" : "lightgrey" }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={dark ? "white" : "black"} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {menuLabel}
                     </div >
-                    // <div className={`menuButton ${type}`} style={{ color, backgroundColor }}>
-                    //     <FontAwesomeIcon className={`fontIconBox-icon-${type}`} icon={icon} color={color} />
-                    //     {label}
-                    // </div>
                 );
                 break;
             default:
-- 
cgit v1.2.3-70-g09d2


From 61420d03c48d67913cd85571088dd31ac88fbb5a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 Aug 2021 15:03:44 -0400
Subject: cleaned up videobox controls along with option to switch to native
 controls.  changed placement of link buttons in lightbox to be outside of
 document

---
 src/Utils.ts                            |  3 +-
 src/client/views/nodes/DocumentView.tsx |  2 +-
 src/client/views/nodes/VideoBox.scss    | 67 ++++++++----------------------
 src/client/views/nodes/VideoBox.tsx     | 72 +++++++++++++++++++--------------
 4 files changed, 60 insertions(+), 84 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 194c38a6f..f251f776c 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -390,8 +390,7 @@ export function formatTime(time: number) {
     const hours = Math.floor(time / 60 / 60);
     const minutes = Math.floor(time / 60) - (hours * 60);
     const seconds = time % 60;
-
-    return hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
+    return (hours ? hours.toString() + ":" : "") + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
 }
 
 export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a29f2f662..d158746d6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -182,7 +182,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
 
-    private get topMost() { return this.props.renderDepth === 0; }
+    private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
     public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); }
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index f593f74fb..cdd36eb3b 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -49,63 +49,28 @@
 //     pointer-events: all;
 // }
 
-.videoBox-time{
-    color : white;
-    top :25px;
-    left : 25px;
+.videoBox-ui {
     position: absolute;
-    background-color: rgba(50, 50, 50, 0.2);
-    transform-origin: left top;
-    pointer-events:all;
+    flex-direction: row;
+    right: 5px;
+    top: 5px;
+    display: none;
+    background-color: rgba(0, 0, 0, 0.6);
 }
-.videoBox-snapshot{
+.videoBox-time, .videoBox-snapshot, .videoBox-timelineButton, .videoBox-play, .videoBox-full {
     color : white;
-    top :25px;
-    right : 25px;
-    position: absolute;
-    background-color:rgba(50, 50, 50, 0.2);
+    position: relative;
     transform-origin: left top;
     pointer-events:all;
-    cursor: default;
-}
-
-.videoBox-timelineButton {
-    position: absolute;
-    display: flex;
-    align-items: center;
-    z-index: 1010;
-    bottom: 5px;
-    right: 5px;
-    color: white;
+    padding-right: 5px;
     cursor: pointer;
-    background: dimgrey;
-    width: 20px;
-    height: 20px;
-}
-.videoBox-play {
-    width: 25px;
-    height: 20px;
-    bottom: 25px;
-    left : 25px;
-    position: absolute;
-    color : white;
-    background-color: rgba(50, 50, 50, 0.2);
-    border-radius: 4px;
-    text-align: center;
-    transform-origin: left bottom;
-    pointer-events:all;
+    &:hover {
+        background-color: gray;
+    }
 }
-.videoBox-full {
-    width: 25px;
-    height: 20px;
-    bottom: 25px;
-    right : 25px;
-    position: absolute;
-    color : white;
-    background-color: rgba(50, 50, 50, 0.2);
-    border-radius: 4px;
-    text-align: center;
-    transform-origin: right bottom;
-    pointer-events:all;
 
+.videoBox:hover {
+    .videoBox-ui {
+        display: flex;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ce45c01e6..fc97a6f4d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -28,6 +28,8 @@ import { LinkDocPreview } from "./LinkDocPreview";
 import "./VideoBox.scss";
 import { DragManager } from "../../util/DragManager";
 import { DocumentManager } from "../../util/DocumentManager";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { Tooltip } from "@material-ui/core";
 const path = require('path');
 
 type VideoDocument = makeInterface<[typeof documentSchema]>;
@@ -49,7 +51,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _playRegionTimer: any = null;
     private _playRegionDuration = 0;
-    @observable static _showControls: boolean;
+    @observable static _nativeControls: boolean;
     @observable _marqueeing: number[] | undefined;
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable _screenCapture = false;
@@ -125,7 +127,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.updateTimecode();
     }
 
-    @action public FullScreen() {
+    @action public FullScreen = () => {
         this._fullScreen = true;
         this.player && this.player.requestFullscreen();
         try {
@@ -137,7 +139,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @action public Snapshot(downX?: number, downY?: number) {
         const width = (this.layoutDoc._width || 0);
-        const height = (this.layoutDoc._height || 0);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
         canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
@@ -281,10 +282,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (field) {
             const url = field.url.href;
             const subitems: ContextMenuProps[] = [];
-            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
-            subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
-            subitems.push({ description: "Take Snapshot", event: () => this.Snapshot(), icon: "expand-arrows-alt" });
-            subitems.push({
+            subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
+            subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
+            this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
                 description: "Screen Capture", event: (async () => {
                     runInAction(() => this._screenCapture = !this._screenCapture);
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
@@ -292,6 +292,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             });
             subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
             subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
+            subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
+            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
@@ -315,7 +317,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
                         style={{ height: "100%", width: "auto", display: "flex", margin: "auto" }}
                         onCanPlay={this.videoLoad}
-                        controls={VideoBox._showControls}
+                        controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
                         onSeeked={this.updateTimecode}
                         onPause={() => this.Pause()}
@@ -380,26 +382,36 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
     private get uIButtons() {
         const curTime = (this.layoutDoc._currentTimecode || 0);
-        return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} >
-            <span>{"" + formatTime(curTime)}</span>
-            <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
-        </div>,
-        <div className="videoBox-snapshot" key="snap" onPointerDown={this.onSnapshotDown} >
-            <FontAwesomeIcon icon="camera" size="lg" />
-        </div>,
-        <div className="videoBox-timelineButton" key="timeline" onPointerDown={this.onTimelineHdlDown} style={{ bottom: `${100 - this.heightPercent}%` }}>
-            <FontAwesomeIcon icon={"eye"} size="lg" />
-        </div>,
-        VideoBox._showControls ? (null) : [
-            // <div className="control-background">
-            <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} >
-                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
-            </div>,
-            <div className="videoBox-full" key="full" onPointerDown={this.onFullDown} >
-                F
-                {/* </div> */}
-            </div>
-        ]]);
+        const nonNativeControls = [
+            <Tooltip title={<div className="dash-tooltip">{"playback"}</div>} placement="bottom">
+                <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} >
+                    <FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
+                </div>
+            </Tooltip>,
+            <Tooltip title={<div className="dash-tooltip">{"timecode"}</div>} placement="bottom">
+                <div className="videoBox-time" onPointerDown={this.onResetDown} >
+                    <span>{formatTime(curTime)}</span>
+                    <span style={{ fontSize: 8 }}>{" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}</span>
+                </div>
+            </Tooltip>,
+            <Tooltip title={<div className="dash-tooltip">{"view full screen"}</div>} placement="bottom">
+                <div className="videoBox-full" onPointerDown={this.FullScreen}>
+                    <FontAwesomeIcon icon="expand" size="lg" />
+                </div>
+            </Tooltip>];
+        return <div className="videoBox-ui">
+            {[...(VideoBox._nativeControls ? [] : nonNativeControls),
+            <Tooltip title={<div className="dash-tooltip">{"snapshot current frame"}</div>} placement="bottom">
+                <div className="videoBox-snapshot" onPointerDown={this.onSnapshotDown} >
+                    <FontAwesomeIcon icon="camera" size="lg" />
+                </div>
+            </Tooltip>,
+            <Tooltip title={<div className="dash-tooltip">{"show annotation timeline"}</div>} placement="bottom">
+                <div className="videoBox-timelineButton" onPointerDown={this.onTimelineHdlDown}>
+                    <FontAwesomeIcon icon="eye" size="lg" />
+                </div>
+            </Tooltip>,]}
+        </div>;
     }
 
     onPlayDown = () => this._playing ? this.Pause() : this.Play();
@@ -453,7 +465,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onPointerLeave={this.updateTimecode}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
-            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}`} />;
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
     }
 
     @action.bound
@@ -610,4 +622,4 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 }
 
-VideoBox._showControls = true;
\ No newline at end of file
+VideoBox._nativeControls = false;
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From e81bb4e087d7bdb9521bfed1c84ba4d4dac8aa75 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 Aug 2021 15:39:19 -0400
Subject: set max size for blue link button when zooming in.

---
 src/client/views/nodes/DocumentView.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d158746d6..678be0500 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -809,7 +809,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
             {this.hideLinkButton ? (null) :
-                <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />}
+                <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().Scale)})` }}>
+                    <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+                </div>}
 
             {audioView}
         </div>;
-- 
cgit v1.2.3-70-g09d2


From 707038795f7129d30a1d4ed5f2311f18a282fecb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Aug 2021 02:30:06 -0400
Subject: fixed following link from ink stroke.  made interacting with
 annotatable documents consistent when selections are cleared by clicking. 
 fixed undo for webboxes.  fixed limiting size of links button.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/views/DocComponent.tsx                  | 10 +++---
 src/client/views/MarqueeAnnotator.tsx              | 16 ++++-----
 src/client/views/PreviewCursor.tsx                 |  2 +-
 src/client/views/collections/CollectionView.tsx    | 10 ++++--
 .../collections/collectionFreeForm/MarqueeView.tsx | 15 +++++---
 src/client/views/nodes/DocumentView.tsx            |  6 ++--
 src/client/views/nodes/ImageBox.tsx                | 25 ++++++++------
 src/client/views/nodes/ScreenshotBox.tsx           |  3 +-
 src/client/views/nodes/VideoBox.tsx                | 19 +++++-----
 src/client/views/nodes/WebBox.tsx                  | 40 +++++++++-------------
 src/client/views/pdf/PDFViewer.tsx                 | 14 ++++----
 12 files changed, 82 insertions(+), 79 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 48886aa3b..f8e9e8702 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -703,6 +703,7 @@ export namespace Docs {
             I.data = new InkField(points);
             I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
+            I.links = ComputedField.MakeFunction("links(self)") as any;
             I[Initializing] = false;
             return I;
         }
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 14d32ef12..d9cc29bed 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -90,9 +90,9 @@ export interface ViewBoxAnnotatableProps {
     renderDepth: number;
     isAnnotationOverlay?: boolean;
 }
-export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T, _annotationKey: string = "annotations") {
+export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
-        @observable _annotationKey: string = _annotationKey;
+        @observable _annotationKeySuffix = () => "annotations";
 
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
@@ -125,7 +125,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-        @computed public get annotationKey() { return this.fieldKey + (this._annotationKey ? "-" + this._annotationKey : ""); }
+        @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? "-" + this._annotationKeySuffix() : ""); }
 
         @action.bound
         removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean {
@@ -203,7 +203,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                         added.map(doc => {
                             if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
-                            if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document;
+                            if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             this.props.layerProvider?.(doc, true);
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
@@ -214,7 +214,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
                             doc.context = this.props.Document;
-                            if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document;
+                            if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
                             inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                         });
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a3a3bce56..c08734c3e 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -43,15 +43,13 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @observable private _width: number = 0;
     @observable private _height: number = 0;
 
-    constructor(props: any) {
-        super(props);
-        runInAction(() => {
-            AnchorMenu.Instance.Status = "marquee";
-            AnchorMenu.Instance.fadeOut(true);
-            // clear out old marquees and initialize menu for new selection
-            Array.from(this.props.savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
-            this.props.savedAnnotations.clear();
-        });
+    @action
+    static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
+        AnchorMenu.Instance.Status = "marquee";
+        AnchorMenu.Instance.fadeOut(true);
+        // clear out old marquees and initialize menu for new selection
+        Array.from(savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
+        savedAnnotations.clear();
     }
 
     @action componentDidMount() {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 679a4b81e..2b82ef475 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -158,7 +158,7 @@ export class PreviewCursor extends React.Component<{}> {
     }
     render() {
         return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) :
-            <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e && e.focus()}
+            <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()}
                 style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
                 I
         </div >;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a82128e47..a821aeeea 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -36,6 +36,7 @@ import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './CollectionView.scss';
+import { returnEmptyString } from '../../../Utils';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -62,7 +63,7 @@ export enum CollectionViewType {
 export interface CollectionViewProps extends FieldViewProps {
     isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     layoutEngine?: () => string;
-    setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+    setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 
     // property overrides for child documents
     children?: never | (() => JSX.Element[]) | React.ReactNode;
@@ -84,7 +85,7 @@ export interface CollectionViewProps extends FieldViewProps {
 type CollectionDocument = makeInterface<[typeof documentSchema]>;
 const CollectionDocument = makeInterface(documentSchema);
 @observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument, "") {
+export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     @observable private static _safeMode = false;
@@ -92,6 +93,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
+    constructor(props: any) {
+        super(props);
+        runInAction(() => this._annotationKeySuffix = returnEmptyString);
+    }
+
     get collectionViewType(): CollectionViewType | undefined {
         const viewField = StrCast(this.layoutDoc._viewType);
         if (CollectionView._safeMode) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 437a3cba8..81f6307d1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -41,7 +41,7 @@ interface MarqueeViewProps {
     trySelectCluster: (addToSel: boolean) => boolean;
     nudge?: (x: number, y: number, nudgeTime?: number) => boolean;
     ungroup?: () => void;
-    setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+    setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 }
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
@@ -211,7 +211,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             // allow marquee if right click OR alt+left click
             if (e.button === 2 || (e.button === 0 && e.altKey)) {
                 // if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
-                this.setPreviewCursor(e.clientX, e.clientY, true);
+                this.setPreviewCursor(e.clientX, e.clientY, true, false);
                 // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
                 e.preventDefault();
                 // }
@@ -284,8 +284,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         else if (document.getSelection()) { document.getSelection()?.empty(); }
     }
 
-    setPreviewCursor = action((x: number, y: number, drag: boolean) => {
-        if (drag) {
+    setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
+        if (hide) {
+            this._downX = this._lastX = x;
+            this._downY = this._lastY = y;
+            this._commandExecuted = false;
+            PreviewCursor.Visible = false;
+        } else if (drag) {
             this._downX = this._lastX = x;
             this._downY = this._lastY = y;
             this._commandExecuted = false;
@@ -313,7 +318,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
-                        this.setPreviewCursor(e.clientX, e.clientY, false);
+                        this.setPreviewCursor(e.clientX, e.clientY, false, false);
                     } else e.stopPropagation();
                 }
             }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 678be0500..121d2d556 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -777,7 +777,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => this.layoutDoc._height = height;
     setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view;
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() ? true : false;
+    isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false;
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? (null) :
@@ -792,7 +792,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.props.contentPointerEvents as any,
+                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
@@ -809,7 +809,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
             {this.hideLinkButton ? (null) :
-                <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().Scale)})` }}>
+                <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().scale(this.props.ContentScaling?.() || 1).Scale)})` }}>
                     <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
                 </div>}
 
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2c0106960..c4e74ebd2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,19 +1,21 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
 import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc';
 import { documentSchema } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { createSchema, makeInterface } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnOne, Utils, returnFalse } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Networking } from '../../Network';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../../views/ContextMenu";
@@ -21,15 +23,13 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
 import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
-import { InkTool } from '../../../fields/InkField';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { AnchorMenu } from '../pdf/AnchorMenu';
-import { Docs } from '../../documents/Documents';
+import { SnappingManager } from '../../util/SnappingManager';
 const path = require('path');
 
 export const pageSchema = createSchema({
@@ -294,7 +294,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             <div className="imageBox-fader" >
                 <img key="paths" ref={this._imgRef}
                     src={srcpath}
-                    style={{ transform, transformOrigin }} draggable={false}
+                    style={{ transform, transformOrigin }}
+                    draggable={false}
                     width={nativeWidth} />
                 {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker">
                     <img className="imageBox-fadeaway" key={"fadeaway"} ref={this._imgRef}
@@ -330,11 +331,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         TraceMobx();
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
-    @action
     marqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
-            this._marqueeing = [e.clientX, e.clientY];
-            e.stopPropagation();
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations)
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
         }
     }
     @action
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index f0db0b594..7ad96bf05 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -175,8 +175,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     @computed get content() {
         if (this.rootDoc.videoWall) return (null);
-        const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-        return <video className={"videoBox-content" + interactive} key="video"
+        return <video className={"videoBox-content"} key="video"
             ref={r => {
                 this._videoRef = r;
                 setTimeout(() => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fc97a6f4d..d4df30b48 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -9,7 +9,7 @@ import { InkTool } from "../../../fields/InkField";
 import { makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { AudioField, nullAudio, VideoField } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils, returnFalse } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -209,11 +209,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-        this._disposers.selection = reaction(() => this.props.isSelected(),
-            selected => !selected && setTimeout(() => {
-                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
-                this._savedAnnotations.clear();
-            }));
         this._disposers.triggerVideo = reaction(
             () => !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined,
             time => time !== undefined && setTimeout(() => {
@@ -550,9 +545,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
     }
 
-    marqueeDown = action((e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) this._marqueeing = [e.clientX, e.clientY];
-    });
+    marqueeDown = (e: React.PointerEvent) => {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        }
+    }
 
     finishMarquee = action(() => {
         this._marqueeing = undefined;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2ff41c73a..0c32a30db 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -44,7 +44,7 @@ const WebDocument = makeInterface(documentSchema);
 export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     public static openSidebarWidth = 250;
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
@@ -52,16 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _keyInput = React.createRef<HTMLInputElement>();
     private _initialScroll: Opt<number>;
     private _sidebarRef = React.createRef<SidebarAnnos>();
-    @observable private _urlHash: string = "";
     @observable private _scrollTimer: any;
     @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
-    @observable private _url: string = "hello";
     @observable private _isAnnotating = false;
     @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = 1500;
+    @computed get _url() { return this.webField?.toString() || ""; }
+    @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
     @computed get scrollHeight() { return this._scrollHeight; }
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
@@ -82,9 +82,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
 
         runInAction(() => {
-            this._url = this.webField?.toString() || "";
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this._annotationKeySuffix = () => this._urlHash + "-annotations";
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
             this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
             this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
@@ -214,6 +212,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
         const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+        this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
+        MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
         this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
         e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
         if (word) {
@@ -306,9 +306,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (future.length) {
             this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
             return true;
         }
         return false;
@@ -321,9 +319,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (history.length) {
             if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
             else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
             return true;
         }
         return false;
@@ -342,17 +338,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"));
             const url = this.webField?.toString();
             if (url && !preview) {
-                if (history === undefined) {
-                    this.dataDoc[this.fieldKey + "-history"] = new List<string>([url]);
-                } else {
-                    this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, url]);
-                }
+                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]);
                 this.layoutDoc._scrollTop = 0;
                 future && (future.length = 0);
             }
-            this._url = newUrl;
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
             if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
         } catch (e) {
             console.log("WebBox URL error:" + this._url);
@@ -413,15 +402,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
-            this._marqueeing = [e.clientX, e.clientY];
-            this.props.select(false);
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
         }
     }
     @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
-        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false);
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
     }
 
     @computed
@@ -511,7 +503,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
     showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ee553fd43..41a60bedf 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -69,7 +69,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _pdfViewer: any;
     private _styleRule: any; // stylesheet rule for making hyperlinks clickable
     private _retries = 0; // number of times tried to create the PDF viewer
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -371,10 +371,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this._downY = e.clientY;
         if ((this.props.Document._viewScale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
-            this._setPreviewCursor?.(e.clientX, e.clientY, true);
+            this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
         if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             this.props.select(false);
+            MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
             if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) {
                 this._textSelecting = false;
@@ -382,10 +383,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             } else {
                 // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
                 setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
-                // clear out old marquees and initialize menu for new selection
-                AnchorMenu.Instance.Status = "marquee";
-                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
-                this._savedAnnotations.clear();
+
                 this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" });
                 document.addEventListener("pointerup", this.onSelectEnd);
                 document.addEventListener("pointermove", this.onSelectMove);
@@ -454,12 +452,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (this._setPreviewCursor && e.button === 0 &&
             Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            this._setPreviewCursor(e.clientX, e.clientY, false);
+            this._setPreviewCursor(e.clientX, e.clientY, false, false);
         }
         // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks
     }
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
 
     getCoverImage = () => {
         if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) {
-- 
cgit v1.2.3-70-g09d2


From addda2b98e450fcad1e2cb328788048ff2fe578f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Aug 2021 12:09:06 -0400
Subject: started to fix doc filters to work on any document

---
 src/client/views/PropertiesView.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx |  2 ++
 src/client/views/nodes/FilterBox.tsx    | 30 +++++++++++++++++++-----------
 3 files changed, 22 insertions(+), 12 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index de437e1df..17b137c31 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -903,7 +903,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (this.selectedDoc.type === DocumentType.COL && !this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+        if (!this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc);
     }
 
     /**
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 121d2d556..bb259da3e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -88,6 +88,8 @@ export interface DocComponentView {
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
     setFocus?: () => void;
+    fieldKey?: string;
+    annotationKey?: string;
 }
 export interface DocumentViewSharedProps {
     renderDepth: number;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index beefc4a82..1e13d1b5a 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -79,10 +79,19 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     */
     @computed static get targetDoc() {
+        return SelectionManager.Views().length ? SelectionManager.Views()[0].Document : CurrentUserUtils.ActiveDashboard;
+    }
+    @computed static get targetDocChildKey() {
+        if (SelectionManager.Views().length) {
+            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || "data";
+        }
+        return "data";
+    }
+    @computed static get targetDocChildren() {
         if (SelectionManager.Views().length) {
-            return SelectionManager.Views()[0]?.Document.type === DocumentType.COL ? SelectionManager.Views()[0].Document : SelectionManager.Views()[0]?.props.ContainingCollectionDoc!;
+            return DocListCast(FilterBox.targetDoc[FilterBox.targetDocChildKey]);
         }
-        return CurrentUserUtils.ActiveDashboard;
+        return DocListCast(CurrentUserUtils.ActiveDashboard.data);
     }
 
     @observable _loaded = false;
@@ -100,7 +109,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         const targetDoc = FilterBox.targetDoc;
         if (this._loaded && targetDoc) {
             const allDocs = new Set<Doc>();
-            const activeTabs = DocListCast(targetDoc.data);
+            const activeTabs = FilterBox.targetDocChildren;
             SearchBox.foreachRecursiveDoc(activeTabs, (depth, doc) => allDocs.add(doc));
             setTimeout(action(() => this._allDocs = Array.from(allDocs)));
         }
@@ -133,8 +142,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         return this.activeAttributes.map(attribute => StrCast(attribute.title));
     }
 
-    gatherFieldValues(dashboard: Doc, facetKey: string) {
-        const childDocs = DocListCast(dashboard.data);
+    gatherFieldValues(childDocs: Doc[], facetKey: string) {
         const valueSet = new Set<string>();
         let rtFields = 0;
         childDocs.forEach((d) => {
@@ -194,13 +202,13 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
      * Responds to clicking the check box in the flyout menu
      */
     facetClick = (facetHeader: string) => {
-        const { targetDoc } = FilterBox;
+        const { targetDoc, targetDocChildren } = FilterBox;
         const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
         if (found !== -1) {
             this.removeFilter(facetHeader);
         } else {
-            const allCollectionDocs = DocListCast((targetDoc.data as any)?.[0].data);
-            const facetValues = this.gatherFieldValues(targetDoc, facetHeader);
+            const allCollectionDocs = targetDocChildren;
+            const facetValues = this.gatherFieldValues(targetDocChildren, facetHeader);
 
             let nonNumbers = 0;
             let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
@@ -248,7 +256,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                 newFacet.layoutKey = "layout";
                 newFacet.type = DocumentType.COL;
                 newFacet.target = targetDoc;
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${facetHeader}")`);
+                newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
             }
             newFacet.hideContextMenu = true;
             Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
@@ -479,9 +487,9 @@ Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader:
     }
     return undefined;
 });
-Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
+Scripting.addGlobal(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
     const allCollectionDocs = new Set<Doc>();
-    const activeTabs = DocListCast(layoutDoc.data);
+    const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
     const set = new Set<string>();
     if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
-- 
cgit v1.2.3-70-g09d2


From 6376a4b6d6a730c6c170fa25dd5f19dea6b8f82a Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 25 Aug 2021 05:20:50 -0400
Subject: many updates

---
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/CurrentUserUtils.ts                |  73 ++++---
 src/client/util/LinkManager.ts                     |   7 +-
 src/client/util/Scripting.ts                       |   6 -
 src/client/views/EditableView.tsx                  |   2 +
 src/client/views/GestureOverlay.tsx                |   5 +-
 src/client/views/InkingStroke.tsx                  |   4 +-
 src/client/views/MainView.tsx                      |   1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 .../collectionLinearView/CollectionLinearView.tsx  |   3 +-
 src/client/views/nodes/DocumentView.tsx            |  12 +-
 src/client/views/nodes/button/FontIconBox.scss     |   3 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 238 ++++++++++++++-------
 .../views/nodes/formattedText/RichTextMenu.tsx     |   1 +
 src/fields/InkField.ts                             |   6 +-
 15 files changed, 234 insertions(+), 133 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1c4b5218c..f3a472329 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -698,14 +698,14 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
             I.layout = InkingStroke.LayoutString("data");
             I.color = color;
             I.fillColor = fillColor;
-            I.strokeWidth = Number(strokeWidth);
+            I.strokeWidth = strokeWidth;
             I.strokeBezier = strokeBezier;
             I.strokeStartMarker = arrowStart;
             I.strokeEndMarker = arrowEnd;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20274672a..7d1cbebdf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -946,21 +946,19 @@ export class CurrentUserUtils {
                 title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
                 list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
                     "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
-                script: 'changeFont'
+                script: 'setFont'
             },
-            { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'},
+            { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize'},
+            { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'setFontColor' },
             { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' },
             { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' },
             { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' },
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
             // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
             // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-            { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'changeAlignment("left")' },
-            { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'changeAlignment("center")' },
-            { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'changeAlignment("right")' },
-
-            { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'changeFontColor' },
-            { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, script:'changeHighlightColor' },
+            { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'setAlignment("left")' },
+            { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'setAlignment("center")' },
+            { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'setAlignment("right")' },
         ];
         return tools;
     }
@@ -971,8 +969,10 @@ export class CurrentUserUtils {
             { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script:'setActiveInkTool("highlighter", true)' },
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' },
             { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
             { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'},
+            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script:'setStrokeColor' },
+
         ];
         return tools;
     }
@@ -1015,18 +1015,18 @@ export class CurrentUserUtils {
                     CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid],
-                script: 'changeView',
-            },
+                script: 'setView',
+            }, // Always show
             {
                 title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
-                script: "changeBackgroundColor"
-            },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay'},
-            { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" },
-            { title: "Text", type: "textTools", subMenu: true },
-            { title: "Ink & GFX", type: "inkTools", subMenu: true },
-            { title: "Web", type: "webTools", subMenu: true },
-            { title: "Schema", type: "schemaTools", subMenu: true }
+                script: "setBackgroundColor", hidden:'selectedDocumentType()'
+            }, // Only when a document is selected
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay', hidden:'selectedDocumentType(undefined, "freeform", true)'}, // Only when floating document is selected in freeform
+            { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden:'selectedDocumentType()' }, // Only when a document is selected 
+            { title: "Text", type: "textTools", subMenu: true, expanded:'selectedDocumentType("rtf")' }, // Always available
+            { title: "Ink & GFX", type: "inkTools", subMenu: true, expanded:'selectedDocumentType("ink")' }, // Always available
+            { title: "Web", type: "webTools", subMenu: true, hidden:'selectedDocumentType("web")' }, // Only when Web is selected
+            { title: "Schema", type: "schemaTools", subMenu: true, hidden:'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
         ];
     }
 
@@ -1034,7 +1034,7 @@ export class CurrentUserUtils {
     static async setupContextMenuButtons(doc: Doc) {
         const docList: Doc[] = [];
 
-        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu }) => {
+        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
             const menuDocList: Doc[] = [];
             if (subMenu) {
                 // default is textTools
@@ -1083,7 +1083,17 @@ export class CurrentUserUtils {
                         onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
                     }));
                 });
-                docList.push(CurrentUserUtils.linearButtonList({ linearViewSubMenu: true, flexGap: 5, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, menuDocList));
+                docList.push(CurrentUserUtils.linearButtonList({ 
+                    linearViewSubMenu: true, 
+                    flexGap: 5, 
+                    ignoreClick: true, 
+                    linearViewExpandable: true, 
+                    icon:title, 
+                    _height: 30, 
+                    backgroundColor: "transparent",
+                    linearViewIsExpanded: expanded ? ComputedField.MakeFunction(expanded) as any : undefined,
+                    hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
+                }, menuDocList));
             } else {
                 docList.push(Docs.Create.FontIconDocument({
                     _nativeWidth: width ? width : 25,
@@ -1105,6 +1115,7 @@ export class CurrentUserUtils {
                     color: Colors.WHITE,
                     backgroundColor: "transparent",
                     _dropAction: "alias",
+                    hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                     onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
                 }));
@@ -1177,7 +1188,7 @@ export class CurrentUserUtils {
         }
         if (doc.myImportPanel === undefined) {
             const uploads = Cast(doc.myImportDocs, Doc, null);
-            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", type: ButtonType.ToolButton, icon: "upload", system: true });
+            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ToolButton, icon: "upload", system: true });
             doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" }));
         }
     }
@@ -1257,8 +1268,8 @@ export class CurrentUserUtils {
         doc._raiseWhenDragged = true;
         doc._showLabel = false;
         doc._showMenuLabel = true;
-        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
-        doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
+        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0, 0)");
+        doc.activeInkWidth = NumCast(doc.activeInkWidth, 1);
         doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
         doc.activeFillColor = StrCast(doc.activeFillColor, "");
         doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
@@ -1279,7 +1290,7 @@ export class CurrentUserUtils {
         doc.filterDocCount = 0;
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupImportSidebar(doc);
+        this.setupImportSidebar(doc); // sets up the import sidebar
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
@@ -1567,3 +1578,15 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
     }
     return [];
 });
+Scripting.addGlobal(function selectedDocumentType(docType?:DocumentType, colType?:CollectionViewType, checkParent?:boolean) {
+    let selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    if (selected && checkParent){
+        const parentDoc: Doc = Cast(selected.context, Doc, null);
+        selected = parentDoc;
+    }
+    console.log(selected, docType, colType);
+    if (selected && docType && selected.type === docType) return false;
+    else if (selected && colType && selected.viewType === colType) return false;
+    else if (selected && !colType && !docType) return false;
+    else return true;
+});
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3579083e4..219c83b12 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -107,7 +107,12 @@ export class LinkManager {
 
     public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
-        return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
+        // FIXME:glr Why is Doc undefined?
+        if (Doc.GetProto(anchor)[DirectLinksSym]){
+            return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
+        } else {
+            return [];
+        }
     } // finds all links that contain the given anchor
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index aa7c23527..efee37419 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -171,18 +171,12 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
             if (!options.editable) {
                 batch = Doc.MakeReadOnly();
             }
-            const result1 = compiledFunction.apply(thisParam, params);
             
             const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
             if (batch) {
                 batch.end();
             }
 
-            if (script.includes('toggleOverlay(true)')){
-                console.log("[Scripting.ts] r1: ", result1);
-                console.log("[Scripting.ts]: ", result);
-            }
-
             return { success: true, result };
         } catch (error) {
 
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index d5f8d3fcf..83336c180 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -155,6 +155,8 @@ export class EditableView extends React.Component<EditableProps> {
         return wasFocused !== this._editing;
     }
 
+    
+
     renderEditor() {
         console.log("render editor", this.props.autosuggestProps);
         return this.props.autosuggestProps
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index bbf21f22c..24f9501ce 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -14,6 +14,7 @@ import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { InteractionUtils } from "../util/InteractionUtils";
 import { Scripting } from "../util/Scripting";
+import { SelectionManager } from "../util/SelectionManager";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
 import "./GestureOverlay.scss";
@@ -23,7 +24,6 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { SelectionManager } from "../util/SelectionManager";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -588,8 +588,9 @@ export class GestureOverlay extends Touchable {
                 this.makePolygon(this.InkShape, false);
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
-                if (!CollectionFreeFormViewChrome.Instance._keepPrimitiveMode) {
+                if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
+                    Doc.UserDoc().activeInkTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 5fc159f14..db09849fd 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -5,7 +5,7 @@ import { Doc } from "../../fields/Doc";
 import { documentSchema } from "../../fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { makeInterface } from "../../fields/Schema";
-import { Cast, StrCast } from "../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../fields/Types";
 import { TraceMobx } from "../../fields/util";
 import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
@@ -168,7 +168,7 @@ export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activ
 export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
 export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
 export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
-export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); }
+export function ActiveInkWidth(): number { return NumCast(ActiveInkPen()?.activeInkWidth); }
 export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
 Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
     CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 75c57909d..dcdf6b9c3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -447,7 +447,6 @@ export class MainView extends React.Component {
         this._panelContent = "none";
         this._sidebarContent.proto = undefined;
         this._flyoutWidth = 0;
-        console.log("close flyout");
     });
 
     remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d09d9b9d7..3c5a63c77 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -506,7 +506,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
-                    { title: "ink stroke", x: B.x - Number(ActiveInkWidth()) / 2, y: B.y - Number(ActiveInkWidth()) / 2, _width: B.width + Number(ActiveInkWidth()), _height: B.height + Number(ActiveInkWidth()) });
+                    { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
                 this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 990c2e4b2..5c16f8929 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -147,8 +147,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                     {this.childLayoutPairs.map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
+                        const hidden = pair.layout.hidden === true;
                         // const scalable = pair.layout.onClick || pair.layout.onDragStart;
-                        return <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
+                        return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
                             style={{
                                 pointerEvents: "all",
                                 width: nested ? undefined : NumCast(pair.layout._width),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6ab25dd6a..5d9f9c6db 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils, returnTrue } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -25,6 +25,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { LinkManager } from '../../util/LinkManager';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from "../../util/SelectionManager";
+import { ColorScheme } from "../../util/SettingsManager";
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from "../../util/Transform";
@@ -41,14 +42,13 @@ import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView
 import { DocumentContentsView } from "./DocumentContentsView";
 import { DocumentLinksButton } from './DocumentLinksButton';
 import "./DocumentView.scss";
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { LinkDocPreview } from "./LinkDocPreview";
-import { PresBox } from './trails/PresBox';
 import { RadialMenu } from './RadialMenu';
-import React = require("react");
 import { ScriptingBox } from "./ScriptingBox";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
-import { ColorScheme } from "../../util/SettingsManager";
+import { PresBox } from './trails/PresBox';
+import React = require("react");
 const { Howl } = require('howler');
 
 interface Window {
@@ -795,7 +795,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
+                pointerEvents: (this.props.contentPointerEvents as any), // || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none")
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index dfb549b95..8de97ea89 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -167,6 +167,7 @@
             position: absolute;
             width: fit-content;
             height: fit-content;
+            color: black;
             top: 100%;
             z-index: 21;
             background-color: #e3e3e3;
@@ -316,7 +317,7 @@
         }
     }
 
-    &.editableTxt {
+    &.editableText {
         cursor: pointer;
         background: transparent;
         width: 100%;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 744064b9f..53cd017b0 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -25,7 +25,9 @@ import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
-import { SetActiveInkWidth } from '../../InkingStroke';
+import { SetActiveInkWidth, SetActiveInkColor } from '../../InkingStroke';
+import { GestureOverlay } from '../../GestureOverlay';
+import { WebField } from '../../../../fields/URLField';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -40,7 +42,7 @@ export enum ButtonType {
     ColorButton = "colorBtn",
     ToolButton = "toolBtn",
     NumberButton = "numBtn",
-    EditableText = "editableTxt"
+    EditableText = "editableText"
 }
 
 export enum NumButtonType {
@@ -115,6 +117,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             const dropdown =
                 <div
                     className="menuButton-dropdownBox"
+                    onPointerDown={e => e.stopPropagation()}
                     style={{ left: 0 }}
                 >
                     <input type="range" step="1" min="0" max="100" value={checkResult}
@@ -240,7 +243,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         let icon: IconProp = "caret-down";
 
 
-        if (script === 'changeView') {
+        if (script === 'setView') {
             const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (selected && StrCast(selected.Document.type) === DocumentType.COL) {
                 text = StrCast(selected.Document._viewType);
@@ -249,23 +252,24 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 text = StrCast(selected.Document.type);
                 icon = Doc.toIcon(selected.Document);
             } else {
+                dropdown = false;
                 text = "None selected";
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
-        } else if (script === 'changeFont') {
+        } else if (script === 'setFont') {
             const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (selected && StrCast(selected.Document.type) === DocumentType.RTF) {
                 text = StrCast(selected.Document._fontFamily);
             } else {
                 const fontFamily = StrCast(Doc.UserDoc()._fontFamily);
-                console.log(fontFamily);
                 text = fontFamily;
             }
             noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
                 "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
         }
 
-        const items = this.buttonList.map((value) => {
+        // Get items to place into the list
+        const list = this.buttonList.map((value) => {
             if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
                 return;
             }
@@ -277,7 +281,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             };
             return <div className="list-item" key={`${value}`}
                 style={{
-                    fontFamily: script === 'changeFont' ? value : undefined,
+                    fontFamily: script === 'setFont' ? value : undefined,
                     backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
                 }}
                 onClick={click}>
@@ -292,22 +296,21 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         return (
             <div className={`menuButton ${this.type} ${active}`}
-                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
+                {dropdown ? (null) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
                 {label}
-                <div
-                    className="menuButton-dropDown"
-                    style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+                {!dropdown ? (null) : <div className="menuButton-dropDown">
                     <FontAwesomeIcon icon={icon} color={color} size="sm" />
-                </div>
+                </div>}
                 {this.rootDoc.dropDownOpen ?
                     <div>
                         <div className="menuButton-dropdownList"
                             style={{ left: 0 }}>
-                            {items}
+                            {list}
                         </div>
                         <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
                     </div>
@@ -323,29 +326,38 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const active: string = StrCast(this.rootDoc.dropDownOpen);
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const numSelected = SelectionManager.Views().length;
-        const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined;
 
         const script: string = StrCast(this.rootDoc.script);
         const scriptCheck: string = script + "(undefined, true)";
         const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result;
-        console.log("[boolResul]: ", boolResult);
+
+        let stroke: boolean = false;
+        let strokeIcon: any;
+        if (script === "setStrokeColor"){
+            stroke = true;
+            const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
+            strokeIcon = (<div style={{borderRadius: "100%", width: Number(checkWidth)+'%', height: Number(checkWidth)+'%', backgroundColor: boolResult }}/>)
+        }
 
         const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-            '#FFFFFF', '#f1efeb', 'transparent'];
+            '#FFFFFF', '#f1efeb'];
 
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={selectedDoc ? selectedDoc._backgroundColor : backgroundColor}
+        const colorBox = (func: (color: ColorState) => void) => <SketchPicker 
+            disableAlpha={!stroke}
+            onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
+
         const dropdownCaret = <div
             className="menuButton-dropDown"
             style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
             <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
         </div>;
+
         const click = (value: ColorState) => {
             const hex: string = value.hex;
             const s = ScriptField.MakeScript(script + '("' + hex + '", false)');
@@ -358,10 +370,10 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                 onPointerDown={e => e.stopPropagation()}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                {stroke ? strokeIcon : <><FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                 <div className="colorButton-color"
-                    style={{ backgroundColor: selectedDoc ? selectedDoc._backgroundColor : backgroundColor }}
-                ></div>
+                    style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }}
+                ></div></>}
                 {label}
                 {/* {dropdownCaret} */}
                 {this.rootDoc.dropDownOpen ?
@@ -408,7 +420,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
-        console.log("switchToggle", switchToggle);
+
         if (switchToggle) {
             return (
                 <div className={`menuButton ${this.type} ${'switch'}`}
@@ -457,17 +469,24 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     }
 
     @computed get editableText() {
+        // Script for running the toggle
+        const script: string = StrCast(this.rootDoc.script);
+
+        // Script for checking the outcome of the toggle
+        let checkScript:string = StrCast(this.rootDoc.script) + "('', true)";
+
+        // Function to run the script
+        const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
+
         const setValue = (value: string, shiftDown?: boolean): boolean => {
-            console.log("setValue");
+            ScriptField.MakeScript(script + "('"+value+"')")?.script.run();
             return true;
         };
-
         return (
-            <div className={`menuButton ${this.type}`}>
-                HELLO
-                {/* <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
-                <EditableView GetValue={() => ""} SetValue={setValue} contents="...">
-                </EditableView> */}
+            <div className="menuButton editableText">
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
+                <EditableView GetValue={() => checkResult} SetValue={setValue} contents="...">
+                </EditableView>
             </div>
         );
     }
@@ -477,12 +496,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         // Variables called through eval (from button)
-        const canUndo: boolean = UndoManager.CanUndo();
-        const canRedo: boolean = UndoManager.CanRedo();
         const numSelected = SelectionManager.Views().length;
-        const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined;
-        const userDoc = Doc.UserDoc();
-
 
         const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark;
 
@@ -503,6 +517,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             case ButtonType.EditableText:
                 console.log("Editable text");
                 button = this.editableText;
+                break;
             case ButtonType.NumberButton:
                 button = this.numberButton;
                 break;
@@ -534,14 +549,6 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                     </div>
                 );
                 break;
-            case ButtonType.DoubleButton:
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {label}
-                    </div>
-                );
-                break;
             case ButtonType.MenuButton:
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
@@ -554,7 +561,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
         }
 
-        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton ? button :
+        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
             <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
                 {button}
             </Tooltip>;
@@ -563,18 +570,25 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeView(view: string) {
+Scripting.addGlobal(function setView(view: string) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
     if (checkResult) {
-        return selected && selected.Document._backgroundColor;
+        if (selected) {
+            console.log("[Background] (selected): " + StrCast(selected._backgroundColor));
+            return selected._backgroundColor;
+        } else {
+            return "#FFFFFF";
+        }
     }
-    selected ? selected.Document._backgroundColor = color : console.log("[FontIconBox.tsx] changeBackgroundColor failed");
+    if (selected && selected.type === DocumentType.INK) selected.fillColor = color;
+    if (selected) selected._backgroundColor = color;
+    Doc.UserDoc()._fontColor = color;
 });
 
 // toggle: Set overlay status of selected document
@@ -586,52 +600,58 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
 });
 
+/** TEXT
+ *      setFont
+ *      setFontSize
+ *      toggleBold
+ *      toggleUnderline
+ *      toggleItalic
+ **/ 
+
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFont(font: string) {
+Scripting.addGlobal(function setFont(font: string) {
     SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font);
     Doc.UserDoc()._fontFamily = font;
-    console.log(Doc.UserDoc()._fontFamily);
     return Doc.UserDoc()._fontFamily;
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
     if (checkResult) {
         if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor));
-            return selected.Document._fontColor;
+            console.log("[Font color] (selected): " + StrCast(selected._fontColor));
+            return selected._fontColor;
         } else {
             console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
             return Doc.UserDoc()._fontColor;
         }
     }
+    if (selected) selected._fontColor = color;
     Doc.UserDoc()._fontColor = color;
 });
 
-// toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontHighlight(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult) {
-        if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor));
-            return selected.Document._fontColor;
-        } else {
-            console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
-            return Doc.UserDoc()._fontColor;
-        }
-    }
-    Doc.UserDoc()._fontColor = color;
-});
+// // toggle: Set overlay status of selected document
+// Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+//     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+//     if (checkResult) {
+//         if (selected) {
+//             return selected.Document._fontColor;
+//         } else {
+//             return Doc.UserDoc()._fontColor;
+//         }
+//     }
+//     Doc.UserDoc()._fontColor = color;
+// });
+
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontSize(size: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) {
     if (checkResult) {
         const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10);
         return size;
     }
-    console.log(size);
     Doc.UserDoc()._fontSize = size + "px";
 });
 
@@ -639,7 +659,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().bold;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.bold = !dv.props.Document.bold);
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().bold = !Doc.UserDoc().bold;
     return Doc.UserDoc().bold;
 });
@@ -648,8 +668,8 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().underline;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.underline = !dv.props.Document.underline);
-    Doc.UserDoc().bold = !Doc.UserDoc().underline;
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+    Doc.UserDoc().underline = !Doc.UserDoc().underline;
     return Doc.UserDoc().underline;
 });
 
@@ -657,17 +677,36 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().italic;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.italic = !dv.props.Document.italic);
-    Doc.UserDoc().bold = !Doc.UserDoc().italic;
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+    Doc.UserDoc().italic = !Doc.UserDoc().italic;
     return Doc.UserDoc().italic;
 });
 
-Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boolean) {
+
+
+
+/** INK
+ *      setActiveInkTool
+ *      setStrokeWidth
+ *      setStrokeColor
+ **/ 
+
+Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().activeInkTool === tool;
+        return Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool;
     }
-    if (tool) {
-        if (Doc.UserDoc().activeInkTool === tool){
+    if (tool === "circle") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool === "square") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool === "line") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool) {
+        if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool){
+            GestureOverlay.Instance.InkShape = "";
             Doc.UserDoc().activeInkTool = InkTool.None;
         } else {
             Doc.UserDoc().activeInkTool = tool;
@@ -679,9 +718,44 @@ Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boole
 
 Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
     if (checkResult) {
-        const width: number = NumCast(Doc.UserDoc().activeInkWidth);
-        return width;
+        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        if (selected && selected.type === DocumentType.INK){
+            return Number(selected.strokeWidth);
+        } else {
+            const width: number = NumCast(Doc.UserDoc().activeInkWidth);
+            return width;
+        }
     }
-    SetActiveInkWidth(StrCast(width));
+    Doc.UserDoc().activeInkWidth = width;
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width));
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
+    if (checkResult) {
+        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        if (selected && selected.type === DocumentType.INK){
+            return selected.color;
+        } else {
+            const color: string = StrCast(Doc.UserDoc().activeInkColor);
+            return color;
+        }
+    }
+    SetActiveInkColor(StrCast(color));
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.color = String(color));
+});
+
+
+/** WEB
+ *      webSetURL
+ **/ 
+Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    console.log("URL: ", url);
+    if (checkResult && selected && selected.type === DocumentType.WEB){
+        return Cast(selected.data, WebField, null).url;
+    }
+    else if (selected && selected.type === DocumentType.WEB){
+        selected.data = url;
+    }
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 82ad2b7db..c081b8e26 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -277,6 +277,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         const found = new Set<Mark>();
         const { from, to } = state.selection as TextSelection;
         state.doc.nodesBetween(from, to, (node) => node.marks.forEach(m => found.add(m)));
+        console.log("Marks: " + found);
         return found;
     }
 
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 1270a2dab..f16e143d8 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,8 +1,8 @@
+import { createSimpleSchema, list, object, serializable } from "serializr";
+import { Scripting } from "../client/util/Scripting";
 import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
-- 
cgit v1.2.3-70-g09d2


From 4aec49631f44d0afb6125b240b504e441ff5cd5c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 25 Aug 2021 16:22:08 -0400
Subject: fixed events for forceActive documents

---
 src/client/views/nodes/DocumentView.tsx | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5d9f9c6db..0d396f8c4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -423,10 +423,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
         LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
-              // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
-              Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
-                this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
-            });        
+        // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
+        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
+            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
+        });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
@@ -767,7 +767,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
-        
+
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
         DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
@@ -780,7 +780,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => this.layoutDoc._height = height;
     setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view;
-    isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false;
+    isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.Document.forceActive || this.props.isContentActive() ? true : false;
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? (null) :
@@ -795,7 +795,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any), // || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none")
+                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
-- 
cgit v1.2.3-70-g09d2


From eeb8b6b1e69c73b822b7e7a67829babb93ca0601 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 25 Aug 2021 21:07:37 -0400
Subject: mini updates

---
 src/client/util/CurrentUserUtils.ts                                 | 6 +++---
 .../views/collections/collectionLinearView/CollectionLinearView.tsx | 2 ++
 src/client/views/nodes/DocumentView.tsx                             | 2 +-
 3 files changed, 6 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6653990bd..fbe216ef1 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -923,7 +923,7 @@ export class CurrentUserUtils {
     }
 
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
-        ...opts, _gridGap: 5, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
+        ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         _lockedPosition: true, linearViewIsExpanded: true, system: true, flexDirection: "row"
     })) as any as Doc
@@ -941,7 +941,7 @@ export class CurrentUserUtils {
             doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true });
         }
         if (doc.dockedBtns === undefined) {
-            doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 5, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
+            doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
         }
         (doc["dockedBtn-undo"] as Doc).dontUndo = true;
         (doc["dockedBtn-redo"] as Doc).dontUndo = true;
@@ -1093,7 +1093,7 @@ export class CurrentUserUtils {
                 });
                 docList.push(CurrentUserUtils.linearButtonList({
                     linearViewSubMenu: true,
-                    flexGap: 5,
+                    flexGap: 0,
                     ignoreClick: true,
                     linearViewExpandable: true,
                     icon: title,
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 5c16f8929..74d581946 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -154,6 +154,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 pointerEvents: "all",
                                 width: nested ? undefined : NumCast(pair.layout._width),
                                 height: nested ? undefined : NumCast(pair.layout._height),
+                                marginLeft: !nested ? 2.5 : 0,
+                                marginRight: !nested ? 2.5 : 0,
                                 // width: NumCast(pair.layout._width),
                                 // height: NumCast(pair.layout._height),
                             }}  >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0d396f8c4..ee3266647 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -795,7 +795,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
+                pointerEvents: (this.props.contentPointerEvents as any),// || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
-- 
cgit v1.2.3-70-g09d2


From 2d8b3c6b73da1b7685903697525a277fd53340a5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 26 Aug 2021 00:32:47 -0400
Subject: a lot of changes to move isContentActive into DocumentView

---
 src/Utils.ts                                       |  2 +-
 src/client/views/DocComponent.tsx                  | 23 ++++++++----------
 src/client/views/collections/CollectionSubView.tsx |  3 +--
 src/client/views/collections/CollectionView.tsx    |  8 ++++++-
 src/client/views/collections/TabDocView.tsx        |  3 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 ++++----
 src/client/views/nodes/AudioBox.tsx                |  8 +++----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  6 +++--
 src/client/views/nodes/ComparisonBox.tsx           | 17 ++++++++++----
 src/client/views/nodes/DocumentView.tsx            | 15 ++++++++----
 src/client/views/nodes/ImageBox.tsx                |  3 +--
 src/client/views/nodes/PDFBox.tsx                  | 11 ++++-----
 src/client/views/nodes/VideoBox.tsx                | 27 +++++++++++-----------
 src/client/views/nodes/WebBox.tsx                  | 12 ++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 10 ++++----
 src/client/views/pdf/AnchorMenu.tsx                |  1 -
 src/client/views/pdf/PDFViewer.tsx                 |  3 +--
 src/fields/Doc.ts                                  |  3 +++
 18 files changed, 91 insertions(+), 74 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 102ac520b..de3b13f63 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -116,7 +116,7 @@ export namespace Utils {
     }
 
     const isTransparentFunctionHack = "isTransparent(__value__)";
-    const noRecursionHack = "__noRecursion";
+    export const noRecursionHack = "__noRecursion";
     export function IsRecursiveFilter(val: string) {
         return !val.includes(noRecursionHack);
     }
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index d9cc29bed..33dff9da5 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,17 +1,17 @@
-import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc';
-import { Touchable } from './Touchable';
-import { computed, action, observable } from 'mobx';
-import { Cast, BoolCast, ScriptCast } from '../../fields/Types';
+import { action, computed, observable } from 'mobx';
+import { DateField } from '../../fields/DateField';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, Opt } from '../../fields/Doc';
 import { InkTool } from '../../fields/InkField';
-import { InteractionUtils } from '../util/InteractionUtils';
 import { List } from '../../fields/List';
-import { DateField } from '../../fields/DateField';
 import { ScriptField } from '../../fields/ScriptField';
-import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail, inheritParentAcls } from '../../fields/util';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { DocUtils } from '../documents/Documents';
+import { Cast, ScriptCast } from '../../fields/Types';
+import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
+import { DocUtils } from '../documents/Documents';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { InteractionUtils } from '../util/InteractionUtils';
 import { UndoManager } from '../util/UndoManager';
+import { Touchable } from './Touchable';
 
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
@@ -107,6 +107,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         // key where data is stored
         @computed get fieldKey() { return this.props.fieldKey; }
 
+        isAnyChildContentActive = () => this._isAnyChildContentActive;
 
         lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
 
@@ -229,10 +230,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         }
 
         whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-        isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None ||
-            (this.props.isContentActive?.() || this.props.Document.forceActive ||
-                this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
-                this.props.rootSelected(outsideReaction)) ? true : false)
     }
     return Component;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1f4c35daa..b70df93da 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -23,6 +23,7 @@ import ReactLoading from 'react-loading';
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     SetSubView?: (subView: any) => void;
+    isAnyChildContentActive: () => boolean;
 }
 
 export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
@@ -490,7 +491,5 @@ import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTex
 import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
 import { SelectionManager } from "../../util/SelectionManager";
 import { OverlayView } from "../OverlayView";
-import { Hypothesis } from "../../util/HypothesisUtils";
 import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
-import { FilterBox } from "../nodes/FilterBox";
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a821aeeea..229e93b9e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -37,6 +37,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './CollectionView.scss';
 import { returnEmptyString } from '../../../Utils';
+import { InkTool } from '../../../fields/InkField';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -119,7 +120,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         // const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
         // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
         // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
-        this.isContentActive();
+        //this.isContentActive();
     }
 
     screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
@@ -253,6 +254,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
     @observable _subView: any = undefined;
 
+    isContentActive = (outsideReaction?: boolean) => {
+        return this.props.isContentActive() ? true : false;
+    }
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
@@ -262,6 +266,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             moveDocument: this.moveDocument,
             removeDocument: this.removeDocument,
             isContentActive: this.isContentActive,
+            isAnyChildContentActive: this.isAnyChildContentActive,
+            whenChildContentsActiveChanged: this.whenChildContentsActiveChanged,
             PanelWidth: this.bodyPanelWidth,
             PanelHeight: this.props.PanelHeight,
             ScreenToLocalTransform: this.screenToLocalTransform,
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e24fcf372..117dba4de 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -480,7 +480,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
                     noOverlay={true} // don't render overlay Docs since they won't scale
                     setHeight={returnFalse}
-                    isContentActive={returnTrue}
+                    isContentActive={returnFalse}
+                    isAnyChildContentActive={returnFalse}
                     select={emptyFunction}
                     dropAction={undefined}
                     isSelected={returnFalse}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b5d9ebd9f..fb949a36d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,7 @@
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
+import { DateField } from "../../../../fields/DateField";
 import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
 import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
 import { Id } from "../../../../fields/FieldSymbols";
@@ -17,6 +18,7 @@ import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUp
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager, dropActionType } from "../../../util/DragManager";
@@ -48,8 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { DateField } from "../../../../fields/DateField";
+import Color = require("color");
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -117,7 +118,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
     @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
-    @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.isContentActive() || this.props.isContentActive()); }
+    @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
     @computed get fitToContentVals() {
         return {
             bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
@@ -170,6 +171,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     getContainerTransform = () => this.cachedGetContainerTransform.copy();
     getTransformOverlay = () => this.getContainerTransform().translate(1, 1);
     getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
+    isAnyChildContentActive = () => this.props.isAnyChildContentActive();
     addLiveTextBox = (newBox: Doc) => {
         FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
         this.addDocument(newBox);
@@ -1033,7 +1035,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
-            isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse}
+            isContentActive={returnFalse}
             isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.focusDocument}
             addDocTab={this.addDocTab}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 82bad971d..faaa887c4 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -267,7 +267,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // returns the html audio element
     @computed get audio() {
-        return <audio ref={this.setRef} className={`audiobox-control${this.isContentActive() ? "-interactive" : ""}`}>
+        return <audio ref={this.setRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
             <source src={this.path} type="audio/mpeg" />
             Not supported.
         </audio>;
@@ -328,6 +328,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             focus={DocUtils.DefaultFocus}
             bringToFront={emptyFunction}
             CollectionView={undefined}
+            isAnyChildContentActive={this.isAnyChildContentActive}
             duration={this.duration}
             playFrom={this.playFrom}
             setTime={this.setAnchorTime}
@@ -337,7 +338,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             ScreenToLocalTransform={this.timelineScreenToLocal}
             Play={this.Play}
             Pause={this.Pause}
-            isContentActive={this.isContentActive}
             playLink={this.playLink}
             PanelWidth={this.timelineWidth}
             PanelHeight={this.timelineHeight}
@@ -345,7 +345,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     render() {
-        const interactive = SnappingManager.GetIsDragging() || this.isContentActive() ? "-interactive" : "";
+        const interactive = SnappingManager.GetIsDragging() || this.props.isContentActive() ? "-interactive" : "";
         return <div className="audiobox-container"
             onContextMenu={this.specificContextMenu}
             onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
@@ -370,7 +370,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                             RECORD
                         </button>}
                 </div> :
-                <div className="audiobox-controls" style={{ pointerEvents: this._isAnyChildContentActive || this.isContentActive() ? "all" : "none" }} >
+                <div className="audiobox-controls" style={{ pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? "all" : "none" }} >
                     <div className="audiobox-dictation" />
                     <div className="audiobox-player" style={{ height: `${AudioBox.heightPercent}%` }} >
                         <div className="audiobox-playhead" style={{ width: AudioBox.playheadWidth }} title={this.mediaState === "paused" ? "play" : "pause"} onClick={this.Play}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.mediaState === "paused" ? "play" : "pause"} size={"1x"} /></div>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 092823603..9cc4b1f9a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,8 +17,8 @@ import { InkingStroke } from "../InkingStroke";
 import { StyleProp } from "../StyleProvider";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
-import { FieldViewProps } from "./FieldView";
 import React = require("react");
+import Color = require("color");
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -164,6 +164,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
         };
+        const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (background && Color(background).alpha() !== 1 ? "multiply" : undefined);
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
@@ -172,7 +174,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 transform: this.transform,
                 transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
-                mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
+                mixBlendMode,
                 display: this.ZInd === -99 ? "none" : undefined
             }} >
             <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 153176afc..6708a08ee 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,17 +1,18 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
-import { Doc } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
 import { documentSchema } from '../../../fields/documentSchemas';
 import { createSchema, makeInterface } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, OmitKeys, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { StyleProp } from '../StyleProvider';
 import "./ComparisonBox.scss";
-import { DocumentView } from './DocumentView';
+import { DocumentView, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import React = require("react");
 
@@ -71,6 +72,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         delete this.dataDoc[fieldKey];
     }
 
+    docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+        if (property === StyleProp.PointerEvents) return "none";
+        return this.props.styleProvider?.(doc, props, property);
+    }
+
     render() {
         const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%";
         const clearButton = (which: string) => {
@@ -84,6 +90,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             const whichDoc = Cast(this.dataDoc[`compareBox-${which}`], Doc, null);
             return whichDoc ? <>
                 <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                    isContentActive={returnFalse}
+                    isDocumentActive={returnFalse}
+                    styleProvider={this.docStyleProvider}
                     Document={whichDoc}
                     DataDoc={undefined}
                     pointerEvents={"none"} />
@@ -102,7 +111,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         };
 
         return (
-            <div className={`comparisonBox${this.isContentActive() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}>
+            <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}>
                 {displayBox("after", 1, this.props.PanelWidth() - 3)}
                 <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}>
                     {displayBox("before", 0, 0)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bb259da3e..11fb5cdb3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -84,6 +84,7 @@ export interface DocComponentView {
     reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
+    isAnyChildContentActive?: () => boolean; // is any child content of the document active
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
@@ -182,7 +183,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
-    _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
+    @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
 
     private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
@@ -778,8 +779,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => this.layoutDoc._height = height;
-    setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view;
-    isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false;
+    setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
+    isContentActive = (outsideReaction?: boolean) => {
+        return CurrentUserUtils.SelectedTool !== InkTool.None ||
+            this.props.Document.forceActive ||
+            this.props.isSelected(outsideReaction) ||
+            this._componentView?.isAnyChildContentActive?.() ||
+            this.props.isContentActive() ? true : false;
+    }
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? (null) :
@@ -794,7 +801,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
+                pointerEvents: (this.props.contentPointerEvents as any) || (this.isContentActive() ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c4e74ebd2..b392ddb95 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -332,7 +332,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations)
                 this._marqueeing = [e.clientX, e.clientY];
@@ -368,7 +368,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 ScreenToLocalTransform={this.screenToLocalTransform}
                 scaling={returnOne}
                 select={emptyFunction}
-                isContentActive={this.isContentActive}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 23236cf20..341fa5919 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -177,9 +177,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         </>;
         const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
         const curPage = this.Document._curPage || 1;
-        return !this.isContentActive() ? (null) :
+        return !this.props.isContentActive() ? (null) :
             <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true}
-                onPointerDown={e => e.stopPropagation()} style={{ display: this.isContentActive() ? "flex" : "none" }}>
+                onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
                 <div className="pdfBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
                     <button className="pdfBox-overlayButton" title={searchTitle} />
                     <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged}
@@ -209,7 +209,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this._pageControls ? pageBtns : (null)}
                 </div>
                 <button className="pdfBox-sidebarBtn" title="Toggle Sidebar"
-                    style={{ display: !this.isContentActive() ? "none" : undefined }}
+                    style={{ display: !this.props.isContentActive() ? "none" : undefined }}
                     onPointerDown={this.sidebarBtnDown} >
                     <FontAwesomeIcon icon={"chevron-left"} size="sm" />
                 </button>
@@ -229,7 +229,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @computed get renderTitleBox() {
-        const classname = "pdfBox" + (this.isContentActive() ? "-interactive" : "");
+        const classname = "pdfBox" + (this.props.isContentActive() ? "-interactive" : "");
         return <div className={classname} >
             <div className="pdfBox-title-outer">
                 <strong className="pdfBox-title" >{this.props.Document.title}</strong>
@@ -268,7 +268,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     dataDoc={this.dataDoc}
                     pdf={this._pdf!}
                     url={this.pdfUrl!.url.pathname}
-                    isContentActive={this.isContentActive}
+                    isContentActive={this.props.isContentActive}
                     anchorMenuClick={this.anchorMenuClick}
                     loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
                     setPdfViewer={this.setPdfViewer}
@@ -291,7 +291,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 sidebarAddDocument={this.sidebarAddDocument}
                 moveDocument={this.moveDocument}
                 removeDocument={this.removeDocument}
-                isContentActive={this.isContentActive}
             />
             {this.settingsPanel()}
         </div>;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d4df30b48..484dec7e2 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -307,7 +307,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
         const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div key="loading">Loading</div> :
-            <div className="container" key="container" style={{ pointerEvents: this._isAnyChildContentActive || this.isContentActive() ? "all" : "none" }}>
+            <div className="container" key="container" style={{ mixBlendMode: "multiply", pointerEvents: this.props.isContentActive() ? "all" : "none" }}>
                 <div className={`${style}`} style={{ width: "100%", height: "100%", left: "0px" }}>
                     <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
                         style={{ height: "100%", width: "auto", display: "flex", margin: "auto" }}
@@ -321,7 +321,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         Not supported.
                     </video>
                     {!this.audiopath || this.audiopath === field.url.href ? (null) :
-                        <audio ref={this.setAudioRef} className={`audiobox-control${this.isContentActive() ? "-interactive" : ""}`}>
+                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
                             <source src={this.audiopath} type="audio/mpeg" />
                         Not supported.
                     </audio>}
@@ -378,30 +378,30 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private get uIButtons() {
         const curTime = (this.layoutDoc._currentTimecode || 0);
         const nonNativeControls = [
-            <Tooltip title={<div className="dash-tooltip">{"playback"}</div>} placement="bottom">
-                <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} >
+            <Tooltip title={<div className="dash-tooltip">{"playback"}</div>} key="play" placement="bottom">
+                <div className="videoBox-play" onPointerDown={this.onPlayDown} >
                     <FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
                 </div>
             </Tooltip>,
-            <Tooltip title={<div className="dash-tooltip">{"timecode"}</div>} placement="bottom">
+            <Tooltip title={<div className="dash-tooltip">{"timecode"}</div>} key="time" placement="bottom">
                 <div className="videoBox-time" onPointerDown={this.onResetDown} >
                     <span>{formatTime(curTime)}</span>
                     <span style={{ fontSize: 8 }}>{" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}</span>
                 </div>
             </Tooltip>,
-            <Tooltip title={<div className="dash-tooltip">{"view full screen"}</div>} placement="bottom">
+            <Tooltip title={<div className="dash-tooltip">{"view full screen"}</div>} key="full" placement="bottom">
                 <div className="videoBox-full" onPointerDown={this.FullScreen}>
                     <FontAwesomeIcon icon="expand" size="lg" />
                 </div>
             </Tooltip>];
         return <div className="videoBox-ui">
             {[...(VideoBox._nativeControls ? [] : nonNativeControls),
-            <Tooltip title={<div className="dash-tooltip">{"snapshot current frame"}</div>} placement="bottom">
+            <Tooltip title={<div className="dash-tooltip">{"snapshot current frame"}</div>} key="snap" placement="bottom">
                 <div className="videoBox-snapshot" onPointerDown={this.onSnapshotDown} >
                     <FontAwesomeIcon icon="camera" size="lg" />
                 </div>
             </Tooltip>,
-            <Tooltip title={<div className="dash-tooltip">{"show annotation timeline"}</div>} placement="bottom">
+            <Tooltip title={<div className="dash-tooltip">{"show annotation timeline"}</div>} key="timeline" placement="bottom">
                 <div className="videoBox-timelineButton" onPointerDown={this.onTimelineHdlDown}>
                     <FontAwesomeIcon icon="eye" size="lg" />
                 </div>
@@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         setupMoveUpEvents(this, e,
             action((e: PointerEvent) => {
                 this._clicking = false;
-                if (this.isContentActive()) {
+                if (this.props.isContentActive()) {
                     const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
                     this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
                 }
@@ -438,7 +438,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             () => {
                 this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
                 setTimeout(action(() => this._clicking = false), 500);
-            }, this.isContentActive(), this.isContentActive());
+            }, this.props.isContentActive(), this.props.isContentActive());
     });
 
     onResetDown = (e: React.PointerEvent) => {
@@ -529,12 +529,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 playFrom={this.playFrom}
                 setTime={this.setAnchorTime}
                 playing={this.playing}
+                isAnyChildContentActive={this.isAnyChildContentActive}
                 whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
                 ScreenToLocalTransform={this.timelineScreenToLocal}
                 Play={this.Play}
                 Pause={this.Pause}
-                isContentActive={this.isContentActive}
                 playLink={this.playLink}
                 PanelHeight={this.timelineHeight}
             />
@@ -546,7 +546,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -570,7 +570,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
     marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
     marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
-    timelineDocFilter = () => ["_timelineLabel:true:x"];
+    timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
     render() {
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
         const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
@@ -592,7 +592,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         ScreenToLocalTransform={this.screenToLocalTransform}
                         docFilters={this.timelineDocFilter}
                         select={emptyFunction}
-                        isContentActive={this.isContentActive}
                         scaling={returnOne}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         removeDocument={this.removeDocument}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ae6b90e41..b484ce302 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -401,7 +401,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -484,7 +484,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             NumCast(this.layoutDoc.nativeWidth)
 
     @computed get content() {
-        return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.isContentActive() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
+        return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
             style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / (this.props.scaling?.() || 1)}%`, }}>
             {this.urlContent}
         </div>;
@@ -529,7 +529,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 docFilters={docFilters || this.props.docFilters}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
-                isContentActive={returnFalse}
                 ContentScaling={returnOne}
                 bringToFront={emptyFunction}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
@@ -539,7 +538,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 childPointerEvents={true}
                 pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
-            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.isContentActive() ? "all" : this.isContentActive() || SnappingManager.GetIsDragging() ? undefined : "none" }} >
+            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.props.isContentActive() ? "all" : this.props.isContentActive() || SnappingManager.GetIsDragging() ? undefined : "none" }} >
                 <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
                     <base target="_blank" />
                     <div className={"webBox-outerContent"} ref={this._outerRef}
@@ -559,7 +558,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 {renderAnnotations(this.transparentFilter)}
                             </div>
                             {renderAnnotations(this.opaqueFilter)}
-                            {renderAnnotations()}
+                            {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
                             {this.annotationLayer}
                         </div>
                     </div>
@@ -588,10 +587,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                    isContentActive={this.isContentActive}
                 />
                 <button className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-                    style={{ display: !this.isContentActive() ? "none" : undefined }}
+                    style={{ display: !this.props.isContentActive() ? "none" : undefined }}
                     onPointerDown={this.sidebarBtnDown} >
                     <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" />
                 </button>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d1027dfd7..4b1d76d00 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if ((e.nativeEvent as any).formattedHandled) {
             console.log("handled");
         }
-        if (!(e.nativeEvent as any).formattedHandled && this.isContentActive(true)) {
+        if (!(e.nativeEvent as any).formattedHandled && this.props.isContentActive(true)) {
             const editor = this._editorView!;
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
             !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
@@ -1481,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @computed get sidebarHandle() {
         TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
-        return (!annotated && !this.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+        return (!annotated && !this.props.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
             style={{
                 left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`,
                 background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
@@ -1504,7 +1504,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                    isContentActive={this.isContentActive}
                 /> :
                 <ComponentTag
                     {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
@@ -1517,7 +1516,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     scaleField={this.SidebarKey + "-scale"}
                     isAnnotationOverlay={false}
                     select={emptyFunction}
-                    isContentActive={this.isContentActive}
                     scaling={this.sidebarContentScaling}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     removeDocument={this.sidebarRemDocument}
@@ -1539,7 +1537,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     render() {
         TraceMobx();
         const selected = this.props.isSelected();
-        const active = this.isContentActive();
+        const active = this.props.isContentActive();
         const scale = this.props.hideOnLeave ? 1 : (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
@@ -1551,7 +1549,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
         return (
             <div className="formattedTextBox-cont"
-                onWheel={e => this.isContentActive() && e.stopPropagation()}
+                onWheel={e => this.props.isContentActive() && e.stopPropagation()}
                 style={{
                     transform: this.props.dontScale ? undefined : `scale(${scale})`,
                     transformOrigin: this.props.dontScale ? undefined : "top left",
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 75e3f81fb..42bec38da 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -69,7 +69,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._disposer = reaction(() => SelectionManager.Views(),
             selected => {
                 this._showLinkPopup = false;
-                console.log("unmount");
                 AnchorMenu.Instance.fadeOut(true)
             });
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 734d9127c..bc35d2126 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -512,7 +512,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const renderAnnotations = (docFilters?: () => string[]) =>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 isAnnotationOverlay={true}
-                isContentActive={returnFalse}
                 fieldKey={this.props.fieldKey + "-annotations"}
                 setPreviewCursor={this.setPreviewCursor}
                 PanelHeight={this.panelHeight}
@@ -543,7 +542,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     transform: `scale(${this._zoomed})`
                 }}>
                 {renderAnnotations(this.opaqueFilter)}
-                {renderAnnotations()}
+                {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
             </div>
         </div>;
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 17f41fac8..b09ff93d0 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1092,6 +1092,9 @@ export namespace Doc {
             const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1);
             return isTransparent(StrCast(doc[key]));
         }
+        if (typeof value === "string") {
+            value = value.replace(`,${Utils.noRecursionHack}`, "");
+        }
         const fieldVal = doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
-- 
cgit v1.2.3-70-g09d2


From 26cd0fb5b14ef0fac6f09228bbca6e16013506e8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 26 Aug 2021 12:08:30 -0400
Subject: allowed tree views to be scaled which makes slides work in lightbox.

---
 src/client/views/collections/CollectionTreeView.tsx | 19 +++++++++++++++++--
 src/client/views/collections/TreeView.tsx           |  4 +++-
 src/client/views/nodes/DocumentView.tsx             |  2 +-
 3 files changed, 21 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e36835893..e94268204 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -61,7 +61,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
             this.props.rootSelected(outsideReaction)) ? true : false)
 
+    isDisposing = false;
     componentWillUnmount() {
+        this.isDisposing = true;
         super.componentWillUnmount();
         this.treedropDisposer?.();
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -76,6 +78,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     refList: Set<any> = new Set();
     observer: any;
     computeHeight = () => {
+        if (this.isDisposing) return;
         const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.paddingTop() + this.paddingBot());
         this.layoutDoc._autoHeightMargins = bodyHeight;
         this.props.setHeight(this.documentTitleHeight() + bodyHeight);
@@ -269,7 +272,16 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             </button>
         </div >;
     }
+    @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
+    @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
 
+    @computed get contentScaling() {
+        const nw = this.nativeWidth;
+        const nh = this.nativeHeight;
+        const hscale = nh ? this.props.PanelHeight() / nh : 1;
+        const wscale = nw ? this.props.PanelWidth() / nw : 1;
+        return wscale < hscale ? wscale : hscale;
+    }
     paddingX = () => NumCast(this.doc._xPadding, 15);
     paddingTop = () => NumCast(this.doc._yPadding, 20);
     paddingBot = () => NumCast(this.doc._yPadding, 20);
@@ -278,14 +290,17 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20));
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
-    panelWidth = () => this.props.PanelWidth() - 2 * this.paddingX();
+    panelWidth = () => (this.props.PanelWidth() - 2 * this.paddingX()) * (this.props.scaling?.() || 1);
     render() {
         TraceMobx();
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
 
         return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
+            <div className="collectionTreeView-container"
+                style={{ transform: `scale(${this.contentScaling})`, transformOrigin: "top left" }}
+
+                onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget"
                     style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
                     onWheel={e => e.stopPropagation()}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5149f7fab..e487106d6 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils, returnOne } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -646,6 +646,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.treeView.props.Document}
+                ContentScaling={returnOne}
             />;
 
         const buttons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""));
@@ -702,6 +703,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             focus={this.refocus}
+            ContentScaling={returnOne}
             hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
             dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
             ScreenToLocalTransform={this.docTransform}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 11fb5cdb3..5bd6049d6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1055,7 +1055,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
             returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
     }
-    @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || [CollectionViewType.Docking, CollectionViewType.Tree].includes(this.Document._viewType as any); }
+    @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any); }
     @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
     @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
-- 
cgit v1.2.3-70-g09d2


From be825152070aa609817a2df7880e9cc377f7cfc1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Sep 2021 12:19:38 -0400
Subject: changed ctrl-click to open alias to reuse existing alias.  changed
 documentview to not clearout docView field when invalidated to allow webBox
 componentView to be persistent which allows proper settting of scrollHeight
 and allows webBox to grow to vertical space available in tab or lightbox.

---
 src/client/views/DocumentDecorations.tsx  | 17 ++++++++---------
 src/client/views/SidebarAnnos.tsx         |  3 ++-
 src/client/views/collections/TreeView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx   |  7 ++++---
 src/client/views/nodes/WebBox.tsx         | 12 ++++++------
 src/fields/documentSchemas.ts             |  1 -
 6 files changed, 21 insertions(+), 21 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e454be618..a570bdb34 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc";
+import { AclAdmin, AclEdit, DataSym, Doc, Field, HeightSym, WidthSym, DocListCast } from "../../fields/Doc";
 import { Document } from '../../fields/documentSchemas';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkField } from "../../fields/InkField";
@@ -159,9 +159,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
         const selectedDocs = SelectionManager.Views();
         if (selectedDocs.length) {
             if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
-                selectedDocs[0].props.Document._fullScreenView = Doc.MakeAlias(selectedDocs[0].props.Document);
-                (selectedDocs[0].props.Document._fullScreenView as Doc).context = undefined;
-                CollectionDockingView.AddSplit(selectedDocs[0].props.Document._fullScreenView as Doc, "right");
+                const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+                CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), "right");
             } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
                 const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
                 alias.context = undefined;
@@ -169,7 +168,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                 alias.y = -alias[HeightSym]() / 2;
                 CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
             } else if (e.altKey) {    // open same document in new tab
-                CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right");
+                CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
             } else {
                 LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document));
             }
@@ -391,10 +390,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
         this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
                 Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                ({
-                    X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
-                    Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
-                })));
+                    ({
+                        X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+                        Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+                    })));
                 Doc.SetNativeWidth(doc, undefined);
                 Doc.SetNativeHeight(doc, undefined);
             });
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 1f9763d18..0e3663f65 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -90,6 +90,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         }
         return this.props.styleProvider?.(doc, props, property);
     }
+    setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
@@ -126,7 +127,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         styleProvider={this.sidebarStyleProvider}
                         docFilters={this.docFilters}
                         scaleField={this.sidebarKey + "-scale"}
-                        setHeight={(height) => this.props.setHeight(height + this.filtersHeight())}
+                        setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         scaling={returnOne}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index e8f345782..b9487054b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -154,7 +154,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         } else {
             // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
             // this.props.addDocTab(CurrentUserUtils.ActiveDashboard.isShared ? Doc.MakeAlias(this.props.document) : this.props.document, "add:right");
-            const bestAlias = DocListCast(this.props.document.aliases).find(doc => !doc.context);
+            const bestAlias = DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
             this.props.addDocTab(bestAlias ?? Doc.MakeAlias(this.props.document), "add:right");
 
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fda84dd2d..6b9513b8b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -93,6 +93,7 @@ export interface DocComponentView {
     fieldKey?: string;
     annotationKey?: string;
     getTitle?: () => string;
+    getScrollHeight?: () => number;
 }
 export interface DocumentViewSharedProps {
     renderDepth: number;
@@ -464,7 +465,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 } else if (!Doc.IsSystem(this.rootDoc)) {
                     UndoManager.RunInBatch(() =>
                         LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.())
-                        //this.props.addDocTab((this.rootDoc._fullScreenView as Doc) || this.rootDoc, "lightbox")
                         , "double tap");
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
@@ -983,6 +983,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
     }
     render() {
+        TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
         const highlightColor = (Doc.UserDoc().colorScheme === ColorScheme.Dark ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
@@ -1075,7 +1076,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); }
     @computed get panelHeight() {
         if (this.effectiveNativeHeight) {
-            return Math.min(this.props.PanelHeight(), Math.max(NumCast(this.layoutDoc.scrollHeight), this.effectiveNativeHeight) * this.nativeScaling);
+            return Math.min(this.props.PanelHeight(), Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight), this.effectiveNativeHeight) * this.nativeScaling);
         }
         return this.props.PanelHeight();
     }
@@ -1182,7 +1183,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         ScreenToLocalTransform={this.screenToLocalTransform}
                         focus={this.props.focus || emptyFunction}
                         bringToFront={emptyFunction}
-                        ref={action((r: DocumentViewInternal | null) => this.docView = r)} />
+                        ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
                 </div>)}
         </div>);
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e4d4557af..719e5a796 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
@@ -13,9 +13,8 @@ import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils, returnEmptyString, returnEmptyFilter } from "../../../Utils";
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
-import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { Scripting } from "../../util/Scripting";
 import { SnappingManager } from "../../util/SnappingManager";
@@ -59,7 +58,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @observable private _scrollHeight = 1500;
+    @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight, 1500);
     @computed get _url() { return this.webField?.toString() || ""; }
     @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
     @computed get scrollHeight() { return this._scrollHeight; }
@@ -229,6 +228,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     }
 
+    getScrollHeight = () => this._scrollHeight;
+
     isFirefox = () => {
         return "InstallTrigger" in window; // navigator.userAgent.indexOf("Chrome") !== -1;
     }
@@ -417,8 +418,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
     }
 
-    @computed
-    get urlContent() {
+    @computed get urlContent() {
         const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index f17a390a6..db2c6ca5b 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -15,7 +15,6 @@ export const documentSchema = createSchema({
     // "Location" properties in a very general sense
     _curPage: "number",         // current page of a page based document
     _currentFrame: "number",    // current frame of a frame based collection (e.g., a progressive slide)
-    _fullScreenView: Doc,       // alias to display when double-clicking to open document in a full-screen view
     lastFrame: "number",        // last frame of a frame based collection (e.g., a progressive slide)
     activeFrame: "number",      // the active frame of a frame based animated document 
     _currentTimecode: "number", // current play back time of a temporal document (video / audio)
-- 
cgit v1.2.3-70-g09d2


From f3a3536ea4b31cf93748490f4b46a56c4775565f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Sep 2021 15:19:38 -0400
Subject: fixed toggleDetail() func to work.

---
 src/client/views/PropertiesButtons.tsx  |  2 +-
 src/client/views/nodes/DocumentView.tsx | 13 ++++++-------
 2 files changed, 7 insertions(+), 8 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 86ef9ab62..dcab9bc1e 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -134,7 +134,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             docView.noOnClick();
             switch (e.target.value) {
                 case "enterPortal": docView.makeIntoPortal(); break;
-                case "toggleDetail": docView.toggleDetail(); break;
+                case "toggleDetail": docView.setToggleDetail(); break;
                 case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break;
                 case "linkOnRight": docView.toggleFollowLink("add:right", false, false); break;
             }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6b9513b8b..20d7a650f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -208,7 +208,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
     @computed get nativeWidth() { return this.props.NativeWidth(); }
     @computed get nativeHeight() { return this.props.NativeHeight(); }
-    @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onfClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
+    @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
     @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
     @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
@@ -605,7 +605,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
 
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
-    @undoBatch toggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`);
+    @undoBatch setToggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${this.Document.layoutKey}")`, { documentView: "any" });
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
@@ -709,7 +709,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
 
                 onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
+                onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${this.Document.layoutKey}")`, { documentView: "any" }), icon: "concierge-bell" });
                 onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -1189,8 +1189,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 }
 
-Scripting.addGlobal(function toggleDetail(doc: any, layoutKey: string, otherKey: string = "layout") {
-    const dv = DocumentManager.Instance.getDocumentView(doc);
-    if (dv?.props.Document.layoutKey === layoutKey) dv?.switchViews(otherKey !== "layout", otherKey.replace("layout_", ""));
-    else dv?.switchViews(true, layoutKey.replace("layout_", ""));
+Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKey: string, primaryLayoutKey: string = "layout") {
+    if (dv.Document.layoutKey === detailLayoutKey) dv.switchViews(primaryLayoutKey !== "layout", primaryLayoutKey.replace("layout_", ""));
+    else dv.switchViews(true, detailLayoutKey.replace("layout_", ""));
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From c8b3de89219a07d466151f5b4633cc87f0d46ec3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Sep 2021 15:32:44 -0400
Subject: from last

---
 src/client/views/nodes/DocumentView.tsx | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 20d7a650f..7463e192f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -605,7 +605,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
 
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
-    @undoBatch setToggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${this.Document.layoutKey}")`, { documentView: "any" });
+    @undoBatch setToggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace("layout_", "")}")`, { documentView: "any" });
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
@@ -709,7 +709,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
 
                 onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${this.Document.layoutKey}")`, { documentView: "any" }), icon: "concierge-bell" });
+                onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
                 onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -1189,7 +1189,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 }
 
-Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKey: string, primaryLayoutKey: string = "layout") {
-    if (dv.Document.layoutKey === detailLayoutKey) dv.switchViews(primaryLayoutKey !== "layout", primaryLayoutKey.replace("layout_", ""));
-    else dv.switchViews(true, detailLayoutKey.replace("layout_", ""));
+Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKey: string) {
+    if (dv.Document.layoutKey === "layout_" + detailLayoutKey) dv.switchViews(false, "layout");
+    else dv.switchViews(true, detailLayoutKey);
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 12d8267533d9b646247914e965b3cf7c32019e4b Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 2 Sep 2021 04:45:01 -0400
Subject: Many updates

- Fixed image uploads so it clones rather than makes an alias
- Updated context menu
- Removed tools panel from novice mode in favour of ":" menu
- Added explainers to menus in novice mode
- Re-added the trails button
- Changed UI for text, PDF & websites so that it is more consistent
---
 src/client/documents/Documents.ts                  |  52 +++++----
 src/client/util/CurrentUserUtils.ts                |  93 ++++++++--------
 src/client/views/ContextMenu.scss                  |   2 +-
 src/client/views/ContextMenuItem.tsx               |   4 +-
 src/client/views/MainViewModal.scss                |   3 +-
 src/client/views/PropertiesButtons.scss            |  41 +++++--
 src/client/views/PropertiesButtons.tsx             |  53 +++++----
 src/client/views/SidebarAnnos.tsx                  |   3 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 .../views/collections/CollectionSchemaView.tsx     |   2 +-
 .../views/collections/CollectionStackingView.scss  |  24 +++++
 .../views/collections/CollectionStackingView.tsx   | 119 +++++++++++++++------
 .../views/collections/CollectionTreeView.tsx       |  95 ++++++++++++----
 src/client/views/collections/TabDocView.tsx        |   3 +-
 src/client/views/collections/TreeView.tsx          |   5 +-
 .../collectionLinearView/CollectionLinearView.scss |   6 +-
 .../collectionSchema/CollectionSchemaHeaders.tsx   |   2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   4 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/PDFBox.scss                 |  16 ++-
 src/client/views/nodes/PDFBox.tsx                  |  13 ++-
 src/client/views/nodes/WebBox.scss                 |   9 +-
 src/client/views/nodes/WebBox.tsx                  |  13 ++-
 src/client/views/nodes/button/FontIconBox.scss     |  29 ++++-
 src/client/views/nodes/button/FontIconBox.tsx      |  15 +--
 .../nodes/formattedText/FormattedTextBox.scss      |  21 +++-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  16 ++-
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/client/views/topbar/TopBar.scss                |   4 +-
 src/client/views/topbar/TopBar.tsx                 |   3 +-
 31 files changed, 460 insertions(+), 202 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c4a713dc6..bd247bd01 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -59,6 +59,7 @@ import { WebBox } from "../views/nodes/WebBox";
 import { SearchBox } from "../views/search/SearchBox";
 import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
 const path = require('path');
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
@@ -272,7 +273,12 @@ export class DocumentOptions {
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
-    treeViewShowClearButton?: boolean; // whether a clear button should be displayed 
+
+    // Action Button
+    buttonMenu?: boolean; // whether a action button should be displayed 
+    buttonMenuDoc?: Doc;
+    explainer?:string;
+
     treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view
     _treeViewOpen?: boolean; // whether this document is expanded in a tree view  (note: need _ and regular versions since this can be specified for both proto and layout docs)
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
@@ -1165,7 +1171,7 @@ export namespace DocUtils {
 
     export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void {
         !simpleMenu && ContextMenu.Instance.addItem({
-            description: "Add Note ...",
+            description: "Quick Notes",
             subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({
                 description: ":" + StrCast(note.title),
                 event: undoBatch((args: { x: number, y: number }) => {
@@ -1178,12 +1184,12 @@ export namespace DocUtils {
                     textDoc[textDoc.layoutKey] = note;
                     docTextAdder(textDoc);
                 }),
-                icon: "eye"
+                icon: StrCast(note.icon) as IconProp
             })) as ContextMenuProps[],
-            icon: "eye"
+            icon: "sticky-note"
         });
-        ContextMenu.Instance.addItem({
-            description: ":=math", event: () => {
+        const math:ContextMenuProps = ({
+            description: ":Math", event: () => {
                 const created = Docs.Create.EquationDocument();
                 if (created) {
                     created.author = Doc.CurrentUserEmail;
@@ -1194,25 +1200,27 @@ export namespace DocUtils {
                     EquationBox.SelectOnLoad = created[Id];
                     docAdder?.(created);
                 }
-            }, icon: "compress-arrows-alt"
+            }, icon: "calculator"
         });
+        const documentList:ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
+            description: ":" + StrCast(dragDoc.title),
+            event: undoBatch((args: { x: number, y: number }) => {
+                const newDoc = Doc.copyDragFactory(dragDoc);
+                if (newDoc) {
+                    newDoc.author = Doc.CurrentUserEmail;
+                    newDoc.x = x;
+                    newDoc.y = y;
+                    if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
+                    docAdder?.(newDoc);
+                }
+            }),
+            icon: Doc.toIcon(dragDoc),
+        })) as ContextMenuProps[]; 
+        documentList.push(math)
         ContextMenu.Instance.addItem({
             description: "Create document",
-            subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
-                description: ":" + StrCast(dragDoc.title),
-                event: undoBatch((args: { x: number, y: number }) => {
-                    const newDoc = Doc.copyDragFactory(dragDoc);
-                    if (newDoc) {
-                        newDoc.author = Doc.CurrentUserEmail;
-                        newDoc.x = x;
-                        newDoc.y = y;
-                        if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
-                        docAdder?.(newDoc);
-                    }
-                }),
-                icon: Doc.toIcon(dragDoc),
-            })) as ContextMenuProps[],
-            icon: "eye"
+            subitems: documentList,
+            icon: "file"
         });
     }// applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
     export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f12ac02f3..f6ec74482 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -84,7 +84,7 @@ export class CurrentUserUtils {
                 title: "NEW MOBILE BUTTON",
                 onClick: undefined,
             },
-                [this.ficon({
+                [this.createToolButton({
                     ignoreClick: true,
                     icon: "mobile",
                     btnType: ButtonType.ToolButton,
@@ -92,7 +92,7 @@ export class CurrentUserUtils {
                 }),
                 this.mobileTextContainer({},
                     [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
-            doc["template-mobile-button"] = CurrentUserUtils.ficon({
+            doc["template-mobile-button"] = CurrentUserUtils.createToolButton({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
                 dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ToolButton,
             });
@@ -107,7 +107,7 @@ export class CurrentUserUtils {
                 { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
             );
             slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
-            doc["template-button-slides"] = CurrentUserUtils.ficon({
+            doc["template-button-slides"] = CurrentUserUtils.createToolButton({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
                 dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card",
                 btnType: ButtonType.ToolButton
@@ -154,7 +154,7 @@ export class CurrentUserUtils {
             };
             linkTemplate.header = new RichTextField(JSON.stringify(rtf2), "");
 
-            doc["template-button-link"] = CurrentUserUtils.ficon({
+            doc["template-button-link"] = CurrentUserUtils.createToolButton({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
                 dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true,
                 btnType: ButtonType.ToolButton
@@ -186,7 +186,7 @@ export class CurrentUserUtils {
             const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true });
             box.isTemplateDoc = makeTemplate(box, true, "switch");
 
-            doc["template-button-switch"] = CurrentUserUtils.ficon({
+            doc["template-button-switch"] = CurrentUserUtils.createToolButton({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
                 dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true,
                 btnType: ButtonType.ToolButton
@@ -236,7 +236,7 @@ export class CurrentUserUtils {
             short.title = "A Short Description";
             long.title = "Long Description";
 
-            doc["template-button-detail"] = CurrentUserUtils.ficon({
+            doc["template-button-detail"] = CurrentUserUtils.createToolButton({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
                 dragFactory: new PrefetchProxy(detailView) as any as Doc,
                 title: "detailView",
@@ -275,7 +275,7 @@ export class CurrentUserUtils {
     static setupNoteTemplates(doc: Doc) {
         if (doc["template-note-Note"] === undefined) {
             const noteView = Docs.Create.TextDocument("", {
-                title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true,
+                title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true, icon: "sticky-note",
                 _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
             });
             noteView.isTemplateDoc = makeTemplate(noteView, true, "Note");
@@ -283,7 +283,7 @@ export class CurrentUserUtils {
         }
         if (doc["template-note-Idea"] === undefined) {
             const noteView = Docs.Create.TextDocument("", {
-                title: "text", backgroundColor: "pink", system: true,
+                title: "text", backgroundColor: "pink", system: true, icon: "lightbulb",
                 _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
             });
             noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea");
@@ -291,7 +291,7 @@ export class CurrentUserUtils {
         }
         if (doc["template-note-Topic"] === undefined) {
             const noteView = Docs.Create.TextDocument("", {
-                title: "text", backgroundColor: "lightblue", system: true,
+                title: "text", backgroundColor: "lightblue", system: true, icon: "book-open",
                 _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
             });
             noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic");
@@ -467,7 +467,7 @@ export class CurrentUserUtils {
             ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyComparison === undefined) {
-            doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "comparison box", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.emptyScript === undefined) {
             doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List<string>(["system"]) });
@@ -512,7 +512,7 @@ export class CurrentUserUtils {
             { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc },
             { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc },
             { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
-            { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc, noviceMode: true },
+            { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
             { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc },
             { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
             { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc },
@@ -561,7 +561,7 @@ export class CurrentUserUtils {
         if (dragCreatorSet === undefined) {
             doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
                 title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
-                _forceActive: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
+                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
             }));
         } else {
@@ -574,29 +574,31 @@ export class CurrentUserUtils {
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
-            { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' },
-            { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
-            { title: "Import", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+            { title: "File Manager", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' },
+            { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
+            { title: "Uploads", target: Cast(doc.myUploadDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
             { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
-            { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
+            { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
         ];
     }
 
     static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (doc.menuStack === undefined) {
             await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) =>
+            const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
                 Docs.Create.FontIconDocument({
                     icon,
                     btnType: ButtonType.MenuButton,
                     _stayInCollection: true,
                     _hideContextMenu: true,
+                    _chromeHidden: true,
                     system: true,
                     dontUndo: true,
                     title,
                     target,
+                    hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
                     _dropAction: "alias",
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                     _width: 60,
@@ -611,8 +613,6 @@ export class CurrentUserUtils {
                     this.searchBtn = menuBtn;
                 }
             });
-            // hack -- last button is assumed to be the userDoc
-            menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()");
 
             menuBtns.forEach(menuBtn => {
                 if (menuBtn.title === "Search") {
@@ -684,7 +684,7 @@ export class CurrentUserUtils {
                 onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
                 backgroundColor: data.backgroundColor, system: true
             },
-                [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
+                [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
         );
     }
 
@@ -820,6 +820,7 @@ export class CurrentUserUtils {
             const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
             (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
             (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "share", "times"]);
             // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
             // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
         }
@@ -860,18 +861,21 @@ export class CurrentUserUtils {
     }
 
     static setupRecentlyClosedDocs(doc: Doc) {
-        // setup Recently Closed library item
         if (doc.myRecentlyClosedDocs === undefined) {
-            const clearDocs = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript(`getProto(self).data = new List([])`), toolTip: "Empty", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.TextButton, _width: 200, buttonText: "Empty Recently Closed", icon: "trash", system: true });
-            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([clearDocs], {
-                title: "My Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true,
+            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
+            const clearDocsButton:Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
+            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
+                explainer: "Documents that you close will appear here so you can easily access them again. You can choose to clear this menu in which case you might lose these documents."
+
             }));
-            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
             (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+            (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Empty recently closed"]);
+            (doc.myRecentlyClosedDocs as any as Doc).contextMenuIcons = new List<string>(["trash"]);
+
         }
     }
 
@@ -916,7 +920,7 @@ export class CurrentUserUtils {
     static async setupSidebarButtons(doc: Doc) {
         CurrentUserUtils.setupSidebarContainer(doc);
         await CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupImportSidebar(doc);
+        CurrentUserUtils.setupUploadSidebar(doc);
         CurrentUserUtils.setupDashboards(doc);
         CurrentUserUtils.setupPresentations(doc);
         CurrentUserUtils.setupFilesystem(doc);
@@ -930,17 +934,17 @@ export class CurrentUserUtils {
         _lockedPosition: true, system: true, flexDirection: "row"
     })) as any as Doc
 
-    static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
-        ...opts, _dropAction: "alias", _removeDropProperties: new List<string>(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true
+    static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
+        ...opts, btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _removeDropProperties: new List<string>(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true
     })) as any as Doc
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupDockedButtons(doc: Doc) {
         if (doc["dockedBtn-undo"] === undefined) {
-            doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true });
+            doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("undo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true });
         }
         if (doc["dockedBtn-redo"] === undefined) {
-            doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true });
+            doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("redo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true });
         }
         if (doc.dockedBtns === undefined) {
             doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
@@ -1039,7 +1043,7 @@ export class CurrentUserUtils {
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
             { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
-            // { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
+            { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
             { title: "Schema", type: "schemaTools", subMenu: true, hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
         ];
     }
@@ -1172,13 +1176,15 @@ export class CurrentUserUtils {
             }
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
+        // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document
         if (doc.mySharedDocs === undefined) {
             let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer");
             if (!sharedDocs) {
                 sharedDocs = Docs.Create.TreeDocument([], {
                     title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
-                    _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+                    _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
                     _chromeHidden: true, boxShadow: "0 0",
+                    explainer: "All of the documents that you receive will appear here. They must be shared by other users. If you receive a Dashboard you can add it to your Dashboards by right clicking and clicking 'Add to Dashboards'"
                 }, sharingDocumentId + "outer", sharingDocumentId);
                 (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
             }
@@ -1189,18 +1195,21 @@ export class CurrentUserUtils {
                 sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
                 sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
                 sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
+                sharedDocs.childContextMenuIcons = new List<string>(["user-plus",]);
+
             }
             doc.mySharedDocs = new PrefetchProxy(sharedDocs);
         }
     }
 
     // Import sidebar is where shared documents are contained
-    static setupImportSidebar(doc: Doc) {
-        if (doc.myImportDocs === undefined) {
-            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Upload from computer", _width: 100, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.TextButton, buttonText: "Upload from computer", icon: "upload", system: true });
-            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([newUpload], {
-                title: "My Uploads", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-                childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+    static setupUploadSidebar(doc: Doc) {
+        if (doc.myUploadDocs === undefined) {
+            const newUploadButton:Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Upload from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.ClickButton, buttonText: "Upload", icon: "upload", system: true });
+            doc.myUploadDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
+                title: "My Uploads", _forceActive: true, buttonMenu: true, buttonMenuDoc: newUploadButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+                childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+                explainer: "All of the documents that are imported into Dash will go here and stay here unless they are explicitly removed."
             }));
         }
     }
@@ -1303,7 +1312,7 @@ export class CurrentUserUtils {
         doc.filterDocCount = 0;
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupImportSidebar(doc); // sets up the import sidebar
+        this.setupUploadSidebar(doc); // sets up the import sidebar
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
@@ -1430,7 +1439,7 @@ export class CurrentUserUtils {
                     }
                 }
             } else if (input.files && input.files.length !== 0) {
-                const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
+                const importDocs = Cast(Doc.UserDoc().myUploadDocs, Doc, null);
                 const disposer = OverlayView.ShowSpinner();
                 DocListCastAsync(importDocs.data).then(async list => {
                     const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 41a6dc569..47ae0424b 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -130,7 +130,7 @@
 }
 
 .contextMenu-inlineMenu {
-    border-top: solid 1px;
+    // border-top: solid 1px; //TODO:glr clean
 }
 
 .contextMenu-item:hover {
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 168fcb888..c3921d846 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -116,12 +116,12 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                     style={{ alignItems: where, borderTop: this.props.addDivider ? "solid 1px" : undefined }}
                     onMouseLeave={this.onPointerLeave} onMouseEnter={this.onPointerEnter}>
                     {this.props.icon ? (
-                        <span className="icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: "center" }}>
+                        <span className="icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: "center", alignSelf: "center" }}>
                             <FontAwesomeIcon icon={this.props.icon} size="sm" />
                         </span>
                     ) : null}
                     <div className="contextMenu-description" onMouseEnter={this.onPointerEnter}
-                        style={{ alignItems: "center" }} >
+                        style={{ alignItems: "center", alignSelf: "center" }} >
                         {this.props.description}
                         <FontAwesomeIcon icon={"angle-right"} size="lg" style={{ position: "absolute", right: "10px" }} />
                     </div>
diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss
index 03cb5cc84..0648e31c5 100644
--- a/src/client/views/MainViewModal.scss
+++ b/src/client/views/MainViewModal.scss
@@ -6,6 +6,7 @@
     height: 100%;
 
     .dialogue-box {
+        padding: 10px;
         position: absolute;
         z-index: 1000;
         text-align: center;
@@ -13,7 +14,7 @@
         align-self: center;
         align-content: center;
         background: white;
-        border-radius: 10px;
+        // border-radius: 10px;
         box-shadow: #00000044 5px 5px 10px;
         transform: translate(-50%, -50%);
         top: 50%;
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 484522bc7..77686a710 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -67,10 +67,12 @@ $linkGap : 3px;
 }
 
 .onClickFlyout-editScript {
+    cursor: pointer;
     text-align: center;
-    border: 0.5px solid grey;
+    margin-top: 5px;
+    border: 0.5px solid $medium-gray;
     background-color: rgb(230, 230, 230);
-    border-radius: 9px;
+    border-radius: 5px;
     padding: 4px;
 }
 
@@ -84,9 +86,32 @@ $linkGap : 3px;
     margin-bottom: 8px;
 }
 
+.propertiesButton-dropdownList {
+    width: 100%;
+    height: fit-content;
+    top: 100%;
+    z-index: 21;
+
+    .list-item {
+        cursor: pointer;
+        color: $black;
+        width: 100%;
+        height: 25px;
+        font-weight: 400;
+        display: flex;
+        justify-content: left;
+        align-items: center;
+        padding-left: 5px;
+    }
+
+    .list-item:hover {
+        background-color: lightgrey;
+    }
+}
+
 .propertiesButtons-title {
-    background: #121721;
-    color: white;
+    background: $dark-gray;
+    color: $white;
     font-size: 6px;
     width: 37px;
     padding: 3px;
@@ -111,17 +136,11 @@ $linkGap : 3px;
     margin-left: 4px;
 
     &:hover {
-        background: $medium-gray;
-        transform: scale(1.05);
+        filter:brightness(0.85);
         cursor: pointer;
     }
 }
 
-.propertiesButtons-linker:hover {
-    cursor: pointer;
-    transform: scale(1.05);
-}
-
 
 @-moz-keyframes spin {
     100% {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index dcab9bc1e..4fde1124f 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -15,6 +15,7 @@ import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
 import './PropertiesButtons.scss';
 import React = require("react");
+import { Colors } from "./global/globalEnums";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -128,11 +129,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @undoBatch
     @action
-    handleOptionChange = (e: any) => {
-        this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value);
+    handleOptionChange = (onClick: string) => {
+        this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick);
         SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => {
             docView.noOnClick();
-            switch (e.target.value) {
+            switch (onClick) {
                 case "enterPortal": docView.makeIntoPortal(); break;
                 case "toggleDetail": docView.setToggleDetail(); break;
                 case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break;
@@ -149,20 +150,34 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @computed
     get onClickFlyout() {
-        const makeLabel = (value: string, label: string) => <div className="radio">
-            <label>
-                <input type="radio" value={value} checked={(this.selectedDoc?.onClickBehavior ?? "nothing") === value} onChange={this.handleOptionChange} />
-                {label}
-            </label>
-        </div>;
+        const buttonList = [
+            ["nothing", "Select Document"],
+            ["enterPortal", "Enter Portal"],
+            ["toggleDetail", "Toggle Detail"],
+            ["linkInPlace", "Follow Link"],
+            ["linkOnRight", "Open Link on Right"]
+        ]
+        const currentSelection = this.selectedDoc.onClickBehavior;
+        // Get items to place into the list
+
+        const list = buttonList.map((value) => {
+            const click = () => {
+                this.handleOptionChange(value[0]);
+            };
+            return <div className="list-item" key={`${value}`}
+                style={{
+                    backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined
+                }}
+                onClick={click}>
+                {value[1]}
+            </div>;
+        });
         return <div>
-            <form>
-                {makeLabel("nothing", "Select Document")}
-                {makeLabel("enterPortal", "Enter Portal")}
-                {makeLabel("toggleDetail", "Toggle Detail")}
-                {makeLabel("linkInPlace", "Follow Link")}
-                {makeLabel("linkOnRight", "Open Link on Right")}
-            </form>
+            <div>
+                <div className="propertiesButton-dropdownList">
+                    {list}
+                </div>
+            </div>
             {Doc.UserDoc().noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
         </div>;
     }
@@ -190,7 +205,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isFreeForm = this.selectedDoc?._viewType === CollectionViewType.Freeform;
         const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree;
         const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => <div className="propertiesButtons-button" style={style}> {ele} </div>;
-
+        const isNovice = Doc.UserDoc().noviceMode;
         return !this.selectedDoc ? (null) :
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
@@ -198,7 +213,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.lockButton)}
                 {toggle(this.dictationButton)}
                 {toggle(this.onClickButton)}
-                {toggle(this.fitWidthButton)}
+                {toggle(this.fitWidthButton, { display: isNovice ? "none" : "" })}
                 {toggle(this.fitContentButton, { display: !isFreeForm ? "none" : "" })}
                 {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
                 {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
@@ -207,7 +222,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.snapButton, { display: isCollection ? "" : "none" })}
                 {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })}
                 {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })}
-                {toggle(this.perspectiveButton, { display: !isCollection ? "none" : "" })}
+                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? "none" : "" })}
             </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 0e3663f65..01b79ffd2 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -106,9 +106,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 {user}
             </div>;
         };
+        // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos
         return !this.props.showSidebar ? (null) :
             <div style={{
-                position: "absolute", pointerEvents: this.props.isContentActive() ? "all" : undefined, top: 0, right: 0,
+                position: "absolute", pointerEvents: this.props.isContentActive() ? "all" : undefined, top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === "title" ? 15 : 0, right: 0,
                 background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
                 width: `${this.panelWidth()}px`,
                 height: "100%"
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index fa35081bc..e528e84e3 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -89,7 +89,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     switch (property.split(":")[0]) {
         case StyleProp.TreeViewIcon: return Doc.toIcon(doc, isOpen);
         case StyleProp.DocContents: return undefined;
-        case StyleProp.WidgetColor: return isAnnotated ? "lightBlue" : darkScheme() ? "lightgrey" : "dimgrey";
+        case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? "lightgrey" : "dimgrey";
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
         case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
         case StyleProp.FontSize: return StrCast(doc?.[fieldKey + "fontSize"]);
@@ -108,7 +108,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.TitleHeight: return 15;
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
-        case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) ||
+        case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
             doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6bdeaf722..6a22acae8 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             {this.renderColors(this._col)}
             <div className="collectionSchema-headerMenu-group">
                 <button onClick={() => { this.deleteColumn(this._col.heading); }}
-                >Delete Column</button>
+                >Hide Column</button>
             </div>
         </div>;
     }
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 4b123c8b6..2f002736d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -14,6 +14,30 @@
     width: 100%;
 }
 
+// TODO:glr Turn this into a seperate class
+.documentButtonMenu {
+    position: relative;
+    height: fit-content;
+    border-bottom: $standard-border;
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    align-items: center;
+    align-content: center;
+    padding: 5px 0 5px 0;
+
+    .documentExplanation {
+        width: 90%;
+        margin: 5px;
+        font-size: 11px;
+        background-color: $light-blue;
+        color: $medium-blue;
+        padding: 10px;
+        border-radius: 10px;
+        border: solid 2px $medium-blue;
+    }
+}
+
 .collectionStackingView,
 .collectionMasonryView {
     height: 100%;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4fedda1bd..3c9084f64 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
 import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
@@ -11,7 +11,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils";
 import { DocUtils, Docs } from "../../documents/Documents";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SnappingManager } from "../../util/SnappingManager";
@@ -23,12 +23,14 @@ import { EditableView } from "../EditableView";
 import { LightboxView } from "../LightboxView";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
+import { StyleProp, DefaultStyleProvider } from "../StyleProvider";
 import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
+import { FontIconBox } from "../nodes/button/FontIconBox";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -514,6 +516,47 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
     }
 
+    @computed get buttonMenu() {
+        const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        // TODO:glr Allow support for multiple buttons
+        if (menuDoc){
+            const width: number = NumCast(menuDoc._width, 30);
+            const height: number = NumCast(menuDoc._height, 30);
+            console.log(menuDoc.title, width, height);
+            return (<div className="buttonMenu-docBtn"
+                style = {{width: width, height: height}}>
+                <DocumentView
+                    Document={menuDoc}
+                    DataDoc={menuDoc}
+                    isContentActive={this.props.isContentActive}
+                    isDocumentActive={returnTrue}
+                    addDocument={this.props.addDocument}
+                    moveDocument={this.props.moveDocument}
+                    addDocTab={this.props.addDocTab}
+                    pinToPres={emptyFunction}
+                    rootSelected={this.props.isSelected}
+                    removeDocument={this.props.removeDocument}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={() => 35}
+                    PanelHeight={() => 35}
+                    renderDepth={this.props.renderDepth}
+                    focus={emptyFunction}
+                    styleProvider={this.props.styleProvider}
+                    layerProvider={this.props.layerProvider}
+                    docViewPath={returnEmptyDoclist}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined} 
+                />
+            </div>
+            );
+        }
+    }
+
 
     @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
     @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
@@ -529,34 +572,50 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             SetValue: this.addGroup,
             contents: "+ ADD A GROUP"
         };
+        const buttonMenu = this.rootDoc.buttonMenu;
+        const noviceExplainer = this.rootDoc.explainer;
+        console.log(noviceExplainer);
         return (
-            <div className="collectionStackingMasonry-cont" >
-                <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
-                    ref={this.createRef}
-                    style={{
-                        overflowY: this.props.isContentActive() ? "auto" : "hidden",
-                        background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                        pointerEvents: this.backgroundEvents ? "all" : undefined
-                    }}
-                    onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
-                    onDrop={this.onExternalDrop.bind(this)}
-                    onContextMenu={this.onContextMenu}
-                    onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
-                    {this.renderedSections}
-                    {!this.showAddAGroup ? (null) :
-                        <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
-                            style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
-                            <EditableView {...editableViewProps} />
-                        </div>}
-                    {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
-                        <Switch
-                            onChange={this.onToggle}
-                            onClick={this.onToggle}
-                            defaultChecked={true}
-                            checkedChildren="edit"
-                            unCheckedChildren="view"
-                        />} */}
-                </div> </div>
+            <>
+                {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
+                    {buttonMenu ? this.buttonMenu : null}
+                    {Doc.UserDoc().isNovice && noviceExplainer ? 
+                        <div className="documentExplanation">
+                            {noviceExplainer}
+                        </div>
+                        : null
+                    }
+                </div> : null}
+                <div className="collectionStackingMasonry-cont" >
+                    <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+                        ref={this.createRef}
+                        style={{
+                            overflowY: this.props.isContentActive() ? "auto" : "hidden",
+                            background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+                            pointerEvents: this.backgroundEvents ? "all" : undefined
+                        }}
+                        onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+                        onDrop={this.onExternalDrop.bind(this)}
+                        onContextMenu={this.onContextMenu}
+                        onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+                        {this.renderedSections}
+                        {!this.showAddAGroup ? (null) :
+                            <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+                                style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+                                <EditableView {...editableViewProps} />
+                            </div>}
+                        {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
+                            <Switch
+                                onChange={this.onToggle}
+                                onClick={this.onToggle}
+                                defaultChecked={true}
+                                checkedChildren="edit"
+                                unCheckedChildren="view"
+                            />} */}
+                    </div> 
+                </div>
+            </>
+            
         );
     }
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 89d42439e..4d62a1af4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { Document, listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyFunction } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -26,6 +26,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
+import { Transform } from '../../util/Transform';
 const _global = (window /* browser */ || global /* node */) as any;
 
 export type collectionTreeViewProps = {
@@ -221,8 +222,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
-        const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
-        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label }));
+        const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+        const icons = StrListCast(this.doc.childContextMenuIcons);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
     @computed get treeViewElements() {
         TraceMobx();
@@ -265,13 +267,48 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         return hideTitle ? (null) : (this.outlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren);
     }
 
-    @computed get renderClearButton() {
-        return !this.doc.treeViewShowClearButton ? (null) : <div key="toolbar">
-            <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
-                <FontAwesomeIcon icon={"trash"} size="sm" />
-            </button>
-        </div >;
+    @computed get buttonMenu() {
+        const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        // To create a multibutton menu add a CollectionLinearView
+        if (menuDoc){
+            
+            const width: number = NumCast(menuDoc._width, 30);
+            const height: number = NumCast(menuDoc._height, 30);
+            console.log(menuDoc.title, width, height);
+            return (<div className="buttonMenu-docBtn"
+                style = {{width: width, height: height}}>
+                <DocumentView
+                    Document={menuDoc}
+                    DataDoc={menuDoc}
+                    isContentActive={this.props.isContentActive}
+                    isDocumentActive={returnTrue}
+                    addDocument={this.props.addDocument}
+                    moveDocument={this.props.moveDocument}
+                    addDocTab={this.props.addDocTab}
+                    pinToPres={emptyFunction}
+                    rootSelected={this.props.isSelected}
+                    removeDocument={this.props.removeDocument}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={() => 35}
+                    PanelHeight={() => 35}
+                    renderDepth={this.props.renderDepth + 1}
+                    focus={emptyFunction}
+                    styleProvider={this.props.styleProvider}
+                    layerProvider={this.props.layerProvider}
+                    docViewPath={returnEmptyDoclist}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined} 
+                />
+            </div>);
+        }
     }
+
+    
     @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
     @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
 
@@ -295,22 +332,34 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         TraceMobx();
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
+        const buttonMenu = this.rootDoc.buttonMenu;
+        const noviceExplainer = this.rootDoc.explainer;
 
         return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            <div className="collectionTreeView-container"
-                style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
-                onContextMenu={this.onContextMenu}>
-                <div className="collectionTreeView-dropTarget"
-                    style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
-                    onWheel={e => e.stopPropagation()}
-                    onDrop={this.onTreeDrop}
-                    ref={this.createTreeDropTarget}>
+               <>
                     {this.titleBar}
-                    {this.renderClearButton}
-                    <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
-                        {this.treeViewElements}
-                    </ul>
-                </div >
-            </div>;
+                    <div className="collectionTreeView-container"
+                        style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
+                        onContextMenu={this.onContextMenu}>
+                        {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
+                            {buttonMenu ? this.buttonMenu : null}
+                            {Doc.UserDoc().noviceMode && noviceExplainer ? 
+                                <div className="documentExplanation">
+                                    {noviceExplainer}
+                                </div>
+                                : null
+                            }
+                        </div> : null}
+                        <div className="collectionTreeView-dropTarget"
+                            style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
+                            onWheel={e => e.stopPropagation()}
+                            onDrop={this.onTreeDrop}
+                            ref={this.createTreeDropTarget}>
+                            <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
+                                {this.treeViewElements}
+                            </ul>
+                        </div >
+                    </div>
+               </>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 889db1ef6..34cb6ec55 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -387,9 +387,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     background={this.miniMapColor}
                     document={this._document}
                     tabView={this.tabView} />
-                <Tooltip style={{ display: this.disableMinimap() ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
+                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
                     <div className="miniMap-hidden"
                         style={{
+                            display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined,
                             color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
                             backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
                             boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index b9487054b..8cf34ddcc 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -525,7 +525,8 @@ export class TreeView extends React.Component<TreeViewProps> {
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
-        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label }));
+        const icons = StrListCast(this.doc.childContextMenuIcons);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
 
@@ -835,7 +836,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         dontRegisterView: boolean | undefined,
         observerHeight: (ref: any) => void,
         unobserveHeight: (ref: any) => void,
-        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[])
+        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[])
     ) {
         const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
index f249eb1f5..8fe804466 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
@@ -98,7 +98,11 @@
             transition: transform 0.2s;
             align-items: center;
             justify-content: center;
-            transition: 0.15s;
+            transition: 0.2s;
+
+            &:hover{
+                filter: brightness(0.85);
+            }
         }
 
         >input {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index b28c32e0e..a25f962df 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -191,7 +191,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
                         {this.renderSorting()}
                         {this.renderColors()}
                         <div className="collectionSchema-headerMenu-group">
-                            <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Delete Column</button>
+                            <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Hide Column</button>
                         </div>
                     </>
                 }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index fed64b620..dfe99ffc8 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             {this.renderColors(this._col)}
             <div className="collectionSchema-headerMenu-group">
                 <button onClick={() => { this.deleteColumn(this._col.heading); }}
-                >Delete Column</button>
+                >Hide Column</button>
             </div>
         </div>;
     }
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index f5a7e61aa..8962d29f0 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -638,12 +638,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
                                 </div>
                             </div>
                         ) : (
-                                <button
+                                <div
                                     className={`audiobox-record${interactive}`}
                                     style={{ backgroundColor: Colors.DARK_GRAY }}
                                 >
                                     RECORD
-                                </button>
+                                </div>
                             )}
                     </div>
                 ) : (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7463e192f..98ae279db 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -635,7 +635,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     makeIntoPortal = async () => {
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
-            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ".portal" });
+            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
             DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
         }
         this.Document.followLinkLocation = "inPlace";
@@ -679,7 +679,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const appearance = cm.findByDescription("UI Controls...");
             const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
             !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
-            appearanceItems.push({
+            !Doc.UserDoc().noviceMode && appearanceItems.push({
                 description: "Add a Field", event: () => {
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 72dec6e4c..f44355929 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,3 +1,5 @@
+@import "../global/globalCssVariables.scss";
+
 .pdfBox,
 .pdfBox-interactive {
     display: inline-block;
@@ -18,12 +20,13 @@
         top: 0;
         left: 0;
 
+        // glr: This should really be the same component as text and PDFs
         .pdfBox-sidebarBtn {
-            background: #121721;
+            background: $black;
             height: 25px;
             width: 25px;
-            right: 0;
-            color: white;
+            right: 5px;
+            color: $white;
             display: flex;
             position: absolute;
             align-items: center;
@@ -31,6 +34,13 @@
             border-radius: 3px;
             pointer-events: all;
             z-index: 1; // so it appears on top of the document's title, if shown
+
+            box-shadow: $standard-box-shadow;
+            transition: 0.2s;
+
+            &:hover{
+                filter: brightness(0.85);
+            }
         }
 
         .pdfBox-pageNums {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5e07229c1..3a399bfdb 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -24,6 +24,7 @@ import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
 import { AnchorMenu } from '../pdf/AnchorMenu';
+import { Colors } from '../global/globalEnums';
 
 type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -208,11 +209,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         onClick={action(() => this._pageControls = !this._pageControls)} />
                     {this._pageControls ? pageBtns : (null)}
                 </div>
-                <button className="pdfBox-sidebarBtn" title="Toggle Sidebar"
-                    style={{ display: !this.props.isContentActive() ? "none" : undefined }}
+                <div className="pdfBox-sidebarBtn" key="sidebar" title="Toggle Sidebar"
+                    style={{ 
+                        display: !this.props.isContentActive() ? "none" : undefined, 
+                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                    }}
                     onPointerDown={this.sidebarBtnDown} >
-                    <FontAwesomeIcon icon={"chevron-left"} size="sm" />
-                </button>
+                        <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                </div>
             </div>;
     }
     sidebarWidth = () => !this.SidebarShown ? 0 :
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 19b69ff5a..417a17d96 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -10,7 +10,7 @@
         background: #121721;
         height: 25px;
         width: 25px;
-        right: 0;
+        right: 5px;
         display: flex;
         position: absolute;
         align-items: center;
@@ -18,6 +18,13 @@
         border-radius: 3px;
         pointer-events: all;
         z-index: 1; // so it appears on top of the document's title, if shown
+
+        box-shadow: $standard-box-shadow;
+        transition: 0.2s;
+
+        &:hover{
+            filter: brightness(0.85);
+        }
     }
 
     .pdfViewerDash-dragAnnotationBox {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 719e5a796..1f85219d6 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -33,6 +33,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from "./LinkDocPreview";
 import "./WebBox.scss";
 import React = require("react");
+import { Colors } from "../global/globalEnums";
 const _global = (window /* browser */ || global /* node */) as any;
 const htmlToText = require("html-to-text");
 
@@ -590,11 +591,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
                 />
-                <button className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-                    style={{ display: !this.props.isContentActive() ? "none" : undefined }}
+                <div className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
+                    style={{ 
+                        display: !this.props.isContentActive() ? "none" : undefined, 
+                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                    }}
                     onPointerDown={this.sidebarBtnDown} >
-                    <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" />
-                </button>
+                        <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                </div>
             </div>);
     }
 }
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index 48fb2c8dc..a2da35fe1 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -7,6 +7,7 @@
     align-items: center;
     font-size: 80%;
     border-radius: $standard-border-radius;
+    transition: 0.15s;
 
     .menuButton-wrap {
         grid-column: 1;
@@ -29,6 +30,9 @@
         font-family: 'ROBOTO';
         text-transform: uppercase;
         font-weight: bold;
+        transition: 0.15s;
+
+
     }
 
     .fontIconBox-icon {
@@ -50,7 +54,18 @@
     }
 
     &.textBtn {
+        display: grid;
+        /* grid-row: auto; */
+        grid-auto-flow: column;
+        cursor: pointer;
         width: 100%;
+        justify-content: center;
+        align-items: center;
+        justify-items: center;
+
+        &:hover {
+            filter:brightness(0.85) !important;
+        }
     }
 
     &.tglBtn {
@@ -127,13 +142,13 @@
         }
 
         &:hover {
-            background-color: rgba(0, 0, 0, 0.3) !important;
+            background-color: rgba(0, 0, 0, 0.3);
         }
     }
 
     &.toolBtn {
         cursor: pointer;
-        width: 40px;
+        width: 100%;
         border-radius: 100%;
 
         svg {
@@ -143,7 +158,7 @@
     }
 
     &.menuBtn {
-        cursor: pointer;
+        cursor: pointer !important;
         border-radius: 0px;
         flex-direction: column;
 
@@ -151,6 +166,10 @@
             width: 45% !important;
             height: 45%;
         }
+        
+        &:hover{
+            filter: brightness(0.85);
+        }
     }
 
 
@@ -163,8 +182,8 @@
 
         .colorButton-color {
             margin-top: 3px;
-            width: 90%;
-            height: 6px;
+            width: 80%;
+            height: 3px;
         }
 
         .menuButton-dropdownBox {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index bccf733f0..1b27d562a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -253,11 +253,12 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             } else if (selected) {
                 dropdown = false;
                 text = StrCast(selected.Document.type);
+                if (text === DocumentType.RTF) text = "Text";
                 icon = Doc.toIcon(selected.Document);
             } else {
                 dropdown = false;
-                noneSelected = true;
-                text = "None selected";
+                icon = "globe-asia";
+                text = "User Default";
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
         } else if (script === 'setFont') {
@@ -302,7 +303,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className={`menuButton ${this.type} ${active}`}
                 style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
-                {dropdown || noneSelected ? (null) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+                {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{marginLeft: 5}} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
@@ -487,7 +488,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             </div>;
 
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
+            <div className="fontIconBox-label" style={{ color: color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
 
@@ -508,7 +509,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         switch (this.type) {
             case ButtonType.TextButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {buttonText ? 
                             <div className="button-text">
@@ -558,7 +559,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
             case ButtonType.MenuButton:
                 const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
-                    style={{ width: 20, height: 20, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
+                    style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
@@ -605,7 +606,7 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
 Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     if (checkResult && selected) {
-        if (NumCast(selected.Document.z) === 1) return Colors.MEDIUM_BLUE;
+        if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
         else return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 3cedab1a4..56f5c5631 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -66,14 +66,25 @@ audiotag:hover {
     
 .formattedTextBox-sidebar-handle {
     position: absolute;
-    top: 0;
+    top: 5px;
     //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
-    width: 10px;
+    width: 25px;
     height: 100%;
-    max-height: 35px;
-    background: lightgray;
-    border-radius: 20px;
+    max-height: 25px;
+    color: white;
+    background: $medium-gray;
+    border-radius: 5px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
     cursor:grabbing;
+    box-shadow: $standard-box-shadow;
+    // transition: 0.2s;
+
+    &:hover{
+        filter: brightness(0.85);
+    }
+
 }
 
 .formattedTextBox-sidebar,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f45b9de7a..468edf6cc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -63,6 +63,8 @@ import { SummaryView } from "./SummaryView";
 import applyDevTools = require("prosemirror-dev-tools");
 import React = require("react");
 import { SidebarAnnos } from '../../SidebarAnnos';
+import { Colors } from '../../global/globalEnums';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
@@ -574,11 +576,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
         const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
+            const icon: IconProp = StrCast(note.icon) as IconProp;
             changeItems.push({
                 description: StrCast(note.title), event: undoBatch(() => {
                     Doc.setNativeView(this.rootDoc);
                     DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
-                }), icon: "eye"
+                }), icon: icon
             });
         });
         !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
@@ -1481,11 +1484,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @computed get sidebarHandle() {
         TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
+        const color = !annotated ? Colors.WHITE : Colors.BLACK;
+        const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
         return (!annotated && !this.props.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
             style={{
-                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`,
-                background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
-            }} />;
+                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 30px"}))`,
+                backgroundColor: backgroundColor,
+                color: color
+            }} >
+                <FontAwesomeIcon icon={"comment-alt"}/>
+            </div>;
     }
     @computed get sidebarCollection() {
         const renderComponent = (tag: string) => {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 260ddfc90..e70b2bc19 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -114,7 +114,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
             const linkFrom = this.props.linkFrom();
             if (linkFrom) {
                 console.log(linkFrom.title);
-                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }, "Link");
             }
         }
     });
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index 05aeb177c..923f1892e 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -33,12 +33,12 @@
             align-self: center;
             border-radius: $standard-border-radius;
             padding: 5px;
-            transition: linear 0.1s;
+            transition: linear 0.2s;
             color: $black;
             background-color: $light-gray;
 
             &:hover {
-                background-color: $light-blue;
+                background-color: darken($color: $light-gray, $amount: 20);
             }
         }
 
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 4dbd5e22c..d5254e315 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -51,7 +51,8 @@ export class TopBar extends React.Component {
                         </div>
                     </div>
                     <div className="topbar-right" >
-                        <div className="topbar-icon">
+                        <div className="topbar-icon" onClick={() => window.open(
+                            "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
                             {"Help"}<FontAwesomeIcon icon="question-circle"></FontAwesomeIcon>
                         </div>
                         <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
-- 
cgit v1.2.3-70-g09d2


From 01cab1fc7259f1a944fad49403328e70e9d61b97 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Sep 2021 10:16:10 -0400
Subject: from last

---
 src/client/views/nodes/DocumentView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7463e192f..c9482c770 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1189,7 +1189,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 }
 
-Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKey: string) {
-    if (dv.Document.layoutKey === "layout_" + detailLayoutKey) dv.switchViews(false, "layout");
-    else dv.switchViews(true, detailLayoutKey);
+Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
+    if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
+    else dv.switchViews(true, detailLayoutKeySuffix);
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 7075cdfdccbbf2aaa174926cfd6ee894c59d5527 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Sep 2021 10:30:40 -0400
Subject: fixed overlay documents not getting pointer events.

---
 src/client/views/nodes/DocumentView.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 501665f3f..3b03643e8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -784,6 +784,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return CurrentUserUtils.SelectedTool !== InkTool.None ||
+            this.props.rootSelected() ||
             this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) ||
             this._componentView?.isAnyChildContentActive?.() ||
-- 
cgit v1.2.3-70-g09d2


From 4d058ec6e2aaa20f240de5d65680e396f035f36e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Sep 2021 12:00:58 -0400
Subject: fixed dragging things into collections.   reactived fitWidth
 properties button

---
 src/client/views/PropertiesButtons.tsx  | 2 +-
 src/client/views/nodes/DocumentView.tsx | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 286365fa6..0bcdbac19 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -213,7 +213,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.lockButton)}
                 {toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
                 {toggle(this.onClickButton)}
-                {toggle(this.fitWidthButton, { display: isNovice ? "none" : "" })}
+                {toggle(this.fitWidthButton)}
                 {toggle(this.fitContentButton, { display: !isFreeForm ? "none" : "" })}
                 {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
                 {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3b03643e8..b2629200d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -784,6 +784,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return CurrentUserUtils.SelectedTool !== InkTool.None ||
+            SnappingManager.GetIsDragging() ||
             this.props.rootSelected() ||
             this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) ||
-- 
cgit v1.2.3-70-g09d2


From 80baae4dcf49f818dca4dbfe340013e0e9d197bb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 3 Sep 2021 03:58:21 -0400
Subject: fixed drawing of link lines - doesn't generate duplicates, and works
 with text regions, scroll locations, and document anchors.

---
 src/client/documents/Documents.ts                  |  9 +++---
 src/client/util/DocumentManager.ts                 | 37 ++++++++++++++--------
 .../CollectionFreeFormLinkView.tsx                 |  8 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 24 ++++++++++----
 src/client/views/nodes/PDFBox.tsx                  | 10 +++---
 src/client/views/nodes/WebBox.tsx                  | 10 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 8 files changed, 63 insertions(+), 39 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bd247bd01..671e27408 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -157,6 +157,7 @@ export class DocumentOptions {
     z?: number; // whether document is in overlay (1) or not (0 or undefined)
     author?: string;
     _layoutKey?: string;
+    unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
     type?: string;
     title?: string;
     "acl-Public"?: string; // public permissions
@@ -277,7 +278,7 @@ export class DocumentOptions {
     // Action Button
     buttonMenu?: boolean; // whether a action button should be displayed 
     buttonMenuDoc?: Doc;
-    explainer?:string;
+    explainer?: string;
 
     treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view
     _treeViewOpen?: boolean; // whether this document is expanded in a tree view  (note: need _ and regular versions since this can be specified for both proto and layout docs)
@@ -1188,7 +1189,7 @@ export namespace DocUtils {
             })) as ContextMenuProps[],
             icon: "sticky-note"
         });
-        const math:ContextMenuProps = ({
+        const math: ContextMenuProps = ({
             description: ":Math", event: () => {
                 const created = Docs.Create.EquationDocument();
                 if (created) {
@@ -1202,7 +1203,7 @@ export namespace DocUtils {
                 }
             }, icon: "calculator"
         });
-        const documentList:ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
+        const documentList: ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
             description: ":" + StrCast(dragDoc.title),
             event: undoBatch((args: { x: number, y: number }) => {
                 const newDoc = Doc.copyDragFactory(dragDoc);
@@ -1215,7 +1216,7 @@ export namespace DocUtils {
                 }
             }),
             icon: Doc.toIcon(dragDoc),
-        })) as ContextMenuProps[]; 
+        })) as ContextMenuProps[];
         documentList.push(math)
         ContextMenu.Instance.addItem({
             description: "Create document",
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index cb0ee411c..b72a3189d 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -25,24 +25,35 @@ export class DocumentManager {
 
     @action
     public AddView = (view: DocumentView) => {
-        DocListCast(view.rootDoc.links).forEach(link => {
-            const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2";
-            const otherDoc = link && (link[whichOtherAnchor] as Doc);
-            const otherDocAnno = DocumentType.MARKER === otherDoc?.type ? otherDoc.annotationOn as Doc : undefined;
-            otherDoc && DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)).
-                forEach(otherView => {
-                    if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) {
-                        this.LinkedDocumentViews.push({ a: whichOtherAnchor === "anchor1" ? otherView : view, b: whichOtherAnchor === "anchor1" ? view : otherView, l: link });
-                    }
-                });
-        });
+        //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
+        if (view.props.LayoutTemplateString?.includes("LinkAnchorBox")) {
+            const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1";
+            view.props.LayoutTemplateString && DocListCast(view.rootDoc.links).forEach(link => {
+                DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).
+                    forEach(otherView => {
+                        this.LinkedDocumentViews.push(
+                            {
+                                a: viewAnchorIndex === "anchor2" ? otherView : view,
+                                b: viewAnchorIndex === "anchor2" ? view : otherView,
+                                l: link
+                            });
+                    });
+            });
+            // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
+            //     view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString));
+        }
         this.DocumentViews.push(view);
     }
     public RemoveView = action((view: DocumentView) => {
+        this.LinkedDocumentViews.slice().forEach(action(pair => {
+            if (pair.a === view || pair.b === view) {
+                const li = this.LinkedDocumentViews.indexOf(pair);
+                li !== -1 && this.LinkedDocumentViews.splice(li, 1);
+            }
+        }));
+
         const index = this.DocumentViews.indexOf(view);
         index !== -1 && this.DocumentViews.splice(index, 1);
-
-        this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null));
     });
 
     //gets all views
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 16258404d..6fe46bbb2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -43,8 +43,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
         setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
         setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
-        const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
-        const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
+        const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+        const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
         const adiv = acont.length ? acont[0] : A.ContentDiv;
         const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
         const a = adiv.getBoundingClientRect();
@@ -140,8 +140,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
         const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
         const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
-        const adiv = (acont.length ? acont[0] : A.ContentDiv);
-        const bdiv = (bcont.length ? bcont[0] : B.ContentDiv);
+        const adiv = acont.length ? acont[0] : A.ContentDiv;
+        const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
         for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
         for (let bpdiv = bdiv; bpdiv; bpdiv = bpdiv.parentElement as any) if ((bpdiv as any).hidden) return;
         const a = adiv.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dd1f3e707..2deaea823 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1052,7 +1052,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             freezeDimensions={this.props.childFreezeDimensions}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
-            dontRegisterView={this.props.dontRegisterView}
+            dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
                 (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b2629200d..2c9daa370 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -847,16 +847,28 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             default: return this.props.styleProvider?.(doc, props, property);
         }
     }
-    @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
+    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to 
+    // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document).  e.g., 
+    //     - PDF text regions are rendered as an Annotations without generating a DocumentView, '
+    //     - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
+    //     - and links to PDF/Web docs at a certain scroll location never create an explicit view.
+    // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
+    @computed get directLinks() {
+        TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(link =>
+            Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
+            Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
+            ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
+            ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
+        );
+    }
     @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
     @computed get allLinkEndpoints() {  // the small blue dots that mark the endpoints of links
         TraceMobx();
-        if (this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
+        if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
         if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
-        // need to use allLinks for RTF since embedded linked text anchors are not rendered with DocumentViews.  All other documents render their anchors with nested DocumentViews so we just need to render the directLinks here
-        const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
+        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
         return filtered.map((link, i) =>
-            <div className="documentView-anchorCont" key={i + 1}>
+            <div className="documentView-anchorCont" key={link[Id]}>
                 <DocumentView {...this.props}
                     Document={link}
                     PanelWidth={this.anchorPanelWidth}
@@ -1154,7 +1166,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
     componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        !this.props.dontRegisterView && DocumentManager.Instance.RemoveView(this);
+        !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
     }
 
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3a399bfdb..2bafb3b7f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -79,9 +79,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor =
             AnchorMenu.Instance?.GetAnchor() ??
             Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
-                annotationOn: this.rootDoc,
+                title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)),
                 y: NumCast(this.layoutDoc._scrollTop),
+                unrendered: true
             });
         this.addDocument(anchor);
         return anchor;
@@ -210,13 +210,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this._pageControls ? pageBtns : (null)}
                 </div>
                 <div className="pdfBox-sidebarBtn" key="sidebar" title="Toggle Sidebar"
-                    style={{ 
-                        display: !this.props.isContentActive() ? "none" : undefined, 
+                    style={{
+                        display: !this.props.isContentActive() ? "none" : undefined,
                         top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
                         backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
                     }}
                     onPointerDown={this.sidebarBtnDown} >
-                        <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
                 </div>
             </div>;
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1f85219d6..eaedd48c7 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -187,8 +187,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
             Docs.Create.WebanchorDocument(this._url, {
                 title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
-                annotationOn: this.rootDoc,
-                y: NumCast(this.layoutDoc._scrollTop)
+                y: NumCast(this.layoutDoc._scrollTop),
+                unrendered: true
             });
         this.addDocumentWrapper(anchor);
         return anchor;
@@ -592,13 +592,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     removeDocument={this.removeDocument}
                 />
                 <div className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-                    style={{ 
-                        display: !this.props.isContentActive() ? "none" : undefined, 
+                    style={{
+                        display: !this.props.isContentActive() ? "none" : undefined,
                         top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
                         backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
                     }}
                     onPointerDown={this.sidebarBtnDown} >
-                        <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
                 </div>
             </div>);
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 59d43f8d7..bc578f95d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -713,7 +713,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             if (sel.from !== sel.to) {
-                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) });
+                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true });
                 const href = targetHref ?? Doc.localServerPath(anchor);
                 if (anchor !== anchorDoc) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-- 
cgit v1.2.3-70-g09d2


From 058cf10e63fe784ba97f288e24fc58d41e0d46f3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Sep 2021 10:56:05 -0400
Subject: made setting background undoable.  added setting header
 color/visibility from main toolbar.  made changing header contents possible
 using #<key>(;<key)* syntax

---
 src/client/util/CurrentUserUtils.ts                |  4 ++++
 src/client/views/nodes/DocumentView.tsx            | 18 +++++++++++++---
 src/client/views/nodes/button/FontIconBox.tsx      | 24 ++++++++++++++--------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 19 +++++++++--------
 4 files changed, 45 insertions(+), 20 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f9c55da29..eb9184e88 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1049,6 +1049,10 @@ export class CurrentUserUtils {
                 title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
                 script: "setBackgroundColor", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
+            {
+                title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
+                script: "setHeaderColor", hidden: 'selectedDocumentType()'
+            }, // Only when a document is selected
             { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2c9daa370..384d2e456 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -952,6 +952,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     onClick={this.onClickFunc}
                 />
             </div>;
+        const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
         const titleView = !showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: this.headerMargin ? "relative" : "absolute",
@@ -960,11 +961,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
             }}>
                 <EditableView ref={this._titleRef}
-                    contents={showTitle === "title" ? StrCast((this.dataDoc || this.props.Document).title) : showTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
+                    contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")}
                     display={"block"}
                     fontSize={10}
-                    GetValue={() => Field.toString((this.dataDoc || this.props.Document)[showTitle.split(";")[0]] as any as Field)}
-                    SetValue={undoBatch((value) => showTitle.includes("Date") ? true : (Doc.GetProto(this.dataDoc || this.props.Document)[showTitle] = value) ? true : true)}
+                    GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
+                    SetValue={undoBatch(value => {
+                        if (value?.startsWith(showTitle + "=")) {
+                            value = value.substring((showTitle + "=").length);
+                            if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
+                            if (showTitle.includes("Date") || showTitle === "author") return true;
+                            return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
+                        } else if (value?.startsWith("#")) {
+                            Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                            return true;
+                        }
+                        return true;
+                    })}
                 />
             </div>;
         return this.props.hideTitle || (!showTitle && !showCaption) ?
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index b72f31ef8..4475cf347 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -15,7 +15,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
 import { Scripting } from "../../../util/Scripting";
 import { SelectionManager } from '../../../util/SelectionManager';
 import { ColorScheme } from '../../../util/SettingsManager';
-import { UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
 import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
@@ -31,6 +31,7 @@ import { ToggleButton } from './toggleButton';
 import { IButtonProps } from './ButtonInterface';
 import { FontIconBadge } from './FontIconBadge';
 import './FontIconBox.scss';
+import { undo } from 'prosemirror-history';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -303,7 +304,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className={`menuButton ${this.type} ${active}`}
                 style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
-                {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{marginLeft: 5}} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+                {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
@@ -348,7 +349,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-            '#FFFFFF', '#f1efeb'];
+            '#FFFFFF', '#f1efeb', "transparent"];
 
         const colorBox = (func: (color: ColorState) => void) => <SketchPicker
             disableAlpha={!stroke}
@@ -369,7 +370,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             const hex: string = value.hex;
             const s = ScriptField.MakeScript(script + '("' + hex + '", false)');
             if (s) {
-                s.script.run().result;
+                undoBatch(() => s.script.run().result)();
             }
         };
         return (
@@ -493,7 +494,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 {this.label}
             </div>;
 
-        const buttonProps:IButtonProps = {
+        const buttonProps: IButtonProps = {
             type: this.type,
             rootDoc: this.rootDoc,
             label: label,
@@ -512,9 +513,9 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {buttonText ? 
+                        {buttonText ?
                             <div className="button-text">
-                                    {buttonText}
+                                {buttonText}
                             </div>
                             : null}
                         {label}
@@ -598,11 +599,18 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
             return "#FFFFFF";
         }
     }
-    if (selected && selected.type === DocumentType.INK) selected.fillColor = color;
+    if (selected?.type === DocumentType.INK) selected.fillColor = color;
     if (selected) selected._backgroundColor = color;
     Doc.UserDoc()._fontColor = color;
 });
 
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
+    Doc.SharingDoc().userColor = undefined;
+    Doc.GetProto(Doc.SharingDoc()).userColor = color;
+    Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate");
+});
+
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bc19320e9..fbb291858 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1484,15 +1484,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
         const color = !annotated ? Colors.WHITE : Colors.BLACK;
         const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
-        return (!annotated && !this.props.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
-            style={{
-                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 17px"}))`,
-                backgroundColor: backgroundColor,
-                color: color,
-                opacity: annotated ? 1 : undefined
-            }} >
-            <FontAwesomeIcon icon={"comment-alt"} />
-        </div>;
+        return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) :
+            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+                style={{
+                    left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 17px"}))`,
+                    backgroundColor: backgroundColor,
+                    color: color,
+                    opacity: annotated ? 1 : undefined
+                }} >
+                <FontAwesomeIcon icon={"comment-alt"} />
+            </div>;
     }
     @computed get sidebarCollection() {
         const renderComponent = (tag: string) => {
-- 
cgit v1.2.3-70-g09d2


From ca8cc74157adb339c15161cacf567c726dd15d1a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Sep 2021 14:00:10 -0400
Subject: from last (fixed text color for headers)

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

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 384d2e456..07e0e2d52 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -953,11 +953,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 />
             </div>;
         const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
+        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
         const titleView = !showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: this.headerMargin ? "relative" : "absolute",
                 height: this.titleHeight,
-                background: StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"),
+                color: lightOrDark(background),
+                background,
                 pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
             }}>
                 <EditableView ref={this._titleRef}
-- 
cgit v1.2.3-70-g09d2


From 57501970d9b56ed39c6b29680286d66a536df5f1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Sep 2021 18:55:32 -0400
Subject: added showTitle prop to allow sidebarAnnos to have a custom title

---
 src/client/views/SidebarAnnos.tsx                         | 10 ++--------
 src/client/views/StyleProvider.tsx                        |  9 ++++++---
 src/client/views/collections/CollectionStackingView.tsx   | 15 ++++++++-------
 .../collections/CollectionStackingViewFieldColumn.tsx     |  2 +-
 src/client/views/collections/CollectionView.tsx           |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx         |  1 +
 src/client/views/nodes/DocumentView.tsx                   | 15 +++++++++++----
 7 files changed, 30 insertions(+), 23 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 01b79ffd2..dd851c764 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -83,13 +83,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-
-    sidebarStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
-        if (property === StyleProp.ShowTitle) {
-            return doc === this.props.rootDoc ? undefined : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title");
-        }
-        return this.props.styleProvider?.(doc, props, property);
-    }
+    showTitle = () => "title";
     setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
@@ -125,13 +119,13 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         NativeHeight={returnZero}
                         PanelHeight={this.panelHeight}
                         PanelWidth={this.panelWidth}
-                        styleProvider={this.sidebarStyleProvider}
                         docFilters={this.docFilters}
                         scaleField={this.sidebarKey + "-scale"}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         scaling={returnOne}
+                        childShowTitle={this.showTitle}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                         childHideDecorationTitle={returnTrue}
                         removeDocument={this.removeDocument}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e528e84e3..3413179ed 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -93,9 +93,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
         case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
         case StyleProp.FontSize: return StrCast(doc?.[fieldKey + "fontSize"]);
-        case StyleProp.ShowTitle: return doc && !doc.presentationTargetDoc && StrCast(doc._showTitle,
-            !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
-                (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
+        case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
+            StrCast(doc._showTitle,
+                props?.showTitle?.() ||
+                (!Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
+                    (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
+                        "author;creationDate") : "")) || "");
         case StyleProp.Color:
             if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c92b259d0..540bfd1ef 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -236,6 +236,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
             dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
             rootSelected={this.rootSelected}
+            showTitle={this.props.childShowTitle}
             dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
@@ -517,14 +518,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     }
 
     @computed get buttonMenu() {
-        const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // TODO:glr Allow support for multiple buttons
-        if (menuDoc){
+        if (menuDoc) {
             const width: number = NumCast(menuDoc._width, 30);
             const height: number = NumCast(menuDoc._height, 30);
             console.log(menuDoc.title, width, height);
             return (<div className="buttonMenu-docBtn"
-                style = {{width: width, height: height}}>
+                style={{ width: width, height: height }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -550,7 +551,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} 
+                    ContainingCollectionDoc={undefined}
                 />
             </div>
             );
@@ -579,7 +580,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().noviceMode && noviceExplainer ? 
+                    {Doc.UserDoc().noviceMode && noviceExplainer ?
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
@@ -612,10 +613,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                                 checkedChildren="edit"
                                 unCheckedChildren="view"
                             />} */}
-                    </div> 
+                    </div>
                 </div>
             </>
-            
+
         );
     }
 }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 47733994b..58289a161 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -111,7 +111,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
     @action pointerLeave = () => this._background = "inherit";
-    textCallback = (char: string) => this.addNewTextDoc("", false, true);
+    textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
 
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 81375e557..bc02c44f0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -71,6 +71,7 @@ export interface CollectionViewProps extends FieldViewProps {
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: () => boolean;
+    childShowTitle?: () => string;
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField, label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 018c2a895..c8561d901 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1052,6 +1052,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             freezeDimensions={this.props.childFreezeDimensions}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
+            showTitle={this.props.childShowTitle}
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
                 (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 07e0e2d52..6eeb51ffe 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,6 +114,7 @@ export interface DocumentViewSharedProps {
     docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
+    showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
@@ -703,9 +704,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
                 const zorders = cm.findByDescription("ZOrder...");
                 const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
-                zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
-                zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
-                zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                if (this.props.bringToFront !== emptyFunction) {
+                    zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                }
                 !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
 
                 onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -974,7 +977,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             if (showTitle.includes("Date") || showTitle === "author") return true;
                             return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
                         } else if (value?.startsWith("#")) {
-                            Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                            if (this.props.showTitle) {
+                                this.rootDoc._showTitle = value?.substring(1) ? value.substring(1) : undefined;
+                            } else {
+                                Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                            }
                             return true;
                         }
                         return true;
-- 
cgit v1.2.3-70-g09d2


From afbbb76afc5a9b1370374b337af1a03a2e94b5d7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Sep 2021 14:46:06 -0400
Subject: fixed anchor menu highlighter button's dropdown location.  fixed
 metadata view for formattedText.  added menu item for metadata view.  fixed
 formattedTextbox initialization.

---
 src/client/util/CurrentUserUtils.ts                | 15 ++++++-----
 src/client/util/Scripting.ts                       |  8 +++---
 src/client/views/DocComponent.tsx                  |  2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  3 ++-
 src/client/views/nodes/DocumentView.tsx            | 20 +++++++-------
 .../views/nodes/formattedText/DashFieldView.tsx    |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 31 +++++++++++++++-------
 .../views/nodes/formattedText/RichTextMenu.scss    |  1 +
 src/client/views/pdf/AnchorMenu.scss               | 29 ++++++++++++++++++++
 src/client/views/pdf/AnchorMenu.tsx                | 13 +++++----
 10 files changed, 86 insertions(+), 38 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index eb9184e88..816503b37 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -22,6 +22,7 @@ import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMu
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
+import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
 import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { LabelBox } from "../views/nodes/LabelBox";
 import { OverlayView } from "../views/OverlayView";
@@ -37,8 +38,6 @@ import { ColorScheme } from "./SettingsManager";
 import { SharingManager } from "./SharingManager";
 import { SnappingManager } from "./SnappingManager";
 import { UndoManager } from "./UndoManager";
-import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
-import { IconName } from "@fortawesome/fontawesome-svg-core";
 
 interface Button {
     title?: string;
@@ -447,16 +446,16 @@ export class CurrentUserUtils {
                 storedMarks: []
             };
             const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
-                title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all",
+                title: "text", version: headerViewVersion, _height: 70, _headerPointerEvents: "all",
                 _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true,
                 cloneFieldFilter: new List<string>(["system"])
             }, "header");
             const headerBtnHgt = 10;
             headerTemplate[DataSym].layout =
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
-                `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'}  height='calc(100% - ${headerBtnHgt}px - {this._headerHeight}px)'/>` +
-                "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'}  dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize}px' height='{(this._headerHeight||1)}px' background='{this._headerColor ||this.target.mySharedDocs.userColor||`lightGray`}' />" +
-                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(1,this._height-30),this._headerHeight===1?50:1)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’}  >Metadata</HTMLdiv>` +
+                `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'}  height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
+                "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'}  dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' background='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
+                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’}  >Metadata</HTMLdiv>` +
                 "</HTMLdiv>";
 
             // "<div style={'height:100%'}>" +
@@ -562,7 +561,7 @@ export class CurrentUserUtils {
         if (dragCreatorSet === undefined) {
             doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
                 title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
-                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true,
+                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
             }));
         } else {
@@ -1554,6 +1553,8 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
         view && SelectionManager.SelectView(view, false);
     }
 });
+Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); },
+    "document containing all shared Docs");
 Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
     "is Dash in novice mode");
 Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index efee37419..40b94024e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -171,7 +171,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
             if (!options.editable) {
                 batch = Doc.MakeReadOnly();
             }
-            
+
             const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
             if (batch) {
                 batch.end();
@@ -316,9 +316,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
     const paramString = paramList.join(", ");
-    const funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
-        ${addReturn ? `return ${script};` : `return ${script};`}
-    })`;
+    const body = addReturn ? `return ${script};` : `return ${script};`;
+    const reqTypes = requiredType ? `: ${requiredType}` : '';
+    const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile("file.ts", funcScript);
 
     if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 33dff9da5..cb36f4270 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -115,7 +115,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
             const style: { [key: string]: any } = {};
             const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
             const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string:  { script }  into a value
-                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || "";
+                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? "";
             };
             divKeys.map((prop: string) => {
                 const p = (this.props as any)[prop];
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 544125ede..fad905d6d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -201,7 +201,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
             if (splits.length > 1) {
                 const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
                 layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
-                return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
+                const script = code[1].value.replace(/^‘/, "").replace(/’$/, "");  // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
+                return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
             }
             return undefined;
             // add input function to props
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6eeb51ffe..6f97cdbd8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -712,7 +712,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
 
                 onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
                 onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -970,19 +970,19 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     display={"block"}
                     fontSize={10}
                     GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
-                    SetValue={undoBatch(value => {
-                        if (value?.startsWith(showTitle + "=")) {
-                            value = value.substring((showTitle + "=").length);
-                            if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
-                            if (showTitle.includes("Date") || showTitle === "author") return true;
-                            return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
-                        } else if (value?.startsWith("#")) {
+                    SetValue={undoBatch(input => {
+                        if (input?.startsWith("#")) {
                             if (this.props.showTitle) {
-                                this.rootDoc._showTitle = value?.substring(1) ? value.substring(1) : undefined;
+                                this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
-                                Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
                             }
                             return true;
+                        } else {
+                            var value = input.replace(new RegExp(showTitle + "="), "");
+                            if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
+                            if (showTitle.includes("Date") || showTitle === "author") return true;
+                            return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
                         }
                         return true;
                     })}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 62f65cdae..34908e54b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         if (this._dashDoc) {
-            const dashVal = this._dashDoc[DataSym][this._fieldKey] ?? this._dashDoc[this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+            const dashVal = this._dashDoc[this._fieldKey] ?? this._dashDoc[DataSym][this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
             const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
             const boolVal = Cast(fval, "boolean", null);
             const strVal = Field.toString(fval as Field) || "";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fbb291858..78de1fd89 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { isEqual } from "lodash";
-import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { baseKeymap, selectAll } from "prosemirror-commands";
 import { history } from "prosemirror-history";
@@ -432,6 +432,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.ProseRef = ele;
+        this.setupEditor(this.config, this.props.fieldKey);
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc));
         // if (this.autoHeight) this.tryUpdateScrollHeight(); 
@@ -573,12 +574,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const cm = ContextMenu.Instance;
 
         const changeItems: ContextMenuProps[] = [];
-        changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
+        changeItems.push({
+            description: "plain", event: undoBatch(() => {
+                Doc.setNativeView(this.rootDoc);
+                this.layoutDoc.autoHeightMargins = undefined;
+            }), icon: "eye"
+        });
+        changeItems.push({
+            description: "metadata", event: undoBatch(() => {
+                this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
+                this.rootDoc.layoutKey = "layout_meta";
+                setTimeout(() => this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50, 50);
+            }), icon: "eye"
+        });
         const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
             const icon: IconProp = StrCast(note.icon) as IconProp;
             changeItems.push({
                 description: StrCast(note.title), event: undoBatch(() => {
+                    this.layoutDoc.autoHeightMargins = undefined;
                     Doc.setNativeView(this.rootDoc);
                     DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
                 }), icon: icon
@@ -864,8 +878,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         );
 
-        this.setupEditor(this.config, this.props.fieldKey);
-
         this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
             search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
             { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
@@ -1144,8 +1156,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
         selectOnLoad && 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.
-        if (!this._editorView!.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
-            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 && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
+            this._editorView.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
         }
     }
 
@@ -1487,7 +1499,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) :
             <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
                 style={{
-                    left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 17px"}))`,
+                    left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
                     backgroundColor: backgroundColor,
                     color: color,
                     opacity: annotated ? 1 : undefined
@@ -1555,7 +1567,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const selPad = Math.min(margins, 10);
         const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
         const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
-        return (
+        const styleFromString = this.styleFromLayoutString(scale);   // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
+        return (styleFromString?.height === "0px" ? (null) :
             <div className="formattedTextBox-cont"
                 onWheel={e => this.props.isContentActive() && e.stopPropagation()}
                 style={{
@@ -1564,7 +1577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     width: this.props.dontScale ? undefined : `${100 / scale}%`,
                     height: this.props.dontScale ? undefined : `${100 / scale}%`,
                     // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
-                    ...this.styleFromLayoutString(scale)   // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
+                    ...styleFromString
                 }}>
                 <div className={`formattedTextBox-cont`} ref={this._ref}
                     style={{
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index c94e93541..8afa0f6b5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -2,6 +2,7 @@
 
 .button-dropdown-wrapper {
     position: relative;
+    display: flex;
 
     .dropdown-button {
         width: 15px;
diff --git a/src/client/views/pdf/AnchorMenu.scss b/src/client/views/pdf/AnchorMenu.scss
index b7afb26a5..6990bdcf1 100644
--- a/src/client/views/pdf/AnchorMenu.scss
+++ b/src/client/views/pdf/AnchorMenu.scss
@@ -4,6 +4,35 @@
     padding: 5px;
     grid-template-columns: 90px 20px 90px;
 }
+.anchorMenu-highlighter {
+    padding-right: 5px;
+    .antimodeMenu-button  {
+        padding: 0;
+        padding: 0;
+        padding-right: 0px;
+        padding-left: 0px;
+        width: 5px;
+    }
+}
+.anchor-color-preview-button {  
+    width: 25px !important;  
+    .anchor-color-preview {
+        display: flex;
+        flex-direction:  column;
+        padding-right: 3px;
+        width: unset !important;
+        .color-preview {
+            width: 60%;
+            top: 80%;
+            height: 4px;
+            position: relative;
+            top: unset;
+            width: 15px;
+            margin-top: 5px;
+            display: block;
+        }
+    }
+}
 
 .color-wrapper {
     display: flex;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 3bf1f0828..3ba427c29 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -41,7 +41,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private highlightColor: string = "rgba(245, 230, 95, 0.616)";
     @observable private _showLinkPopup: boolean = false;
 
-    @observable public _colorBtn = false;
     @observable public Highlighting: boolean = false;
     @observable public Status: "marquee" | "annotation" | "" = "";
 
@@ -97,9 +96,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @computed get highlighter() {
         const button =
-            <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
-                <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
-                <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
+            <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
+                <div className="anchor-color-preview" >
+                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
+                    <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
+                </div>
             </button>;
 
         const dropdownContent =
@@ -117,7 +118,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             </div>;
         return (
             <Tooltip key="highlighter" title={<div className="dash-tooltip">{"Click to Highlight"}</div>}>
-                <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
+                <div className="anchorMenu-highlighter">
+                    <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
+                </div>
             </Tooltip>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 9d177851ecaaea1a89ac9b7de1cec16368603eaa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Sep 2021 22:49:13 -0400
Subject: added SelectionManager.Docs() to avoid having to dereference props to
 get at the seelcted Doc.  this was causing a cycle in menu items when
 selected.  fixed sizing of filterBox.  enabled showing titles for VID,COL,and
 IMG in addition to RTF.

---
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/util/HypothesisUtils.ts                 |  2 +-
 src/client/util/SelectionManager.ts                | 12 ++--
 src/client/views/GlobalKeyHandler.ts               |  4 +-
 src/client/views/PropertiesView.tsx                |  2 +-
 src/client/views/StyleProvider.tsx                 |  2 +-
 src/client/views/collections/CollectionMenu.tsx    | 21 +++----
 src/client/views/collections/CollectionSubView.tsx |  8 +--
 .../collectionLinear/CollectionLinearView.tsx      | 19 +++---
 src/client/views/nodes/DocumentView.tsx            |  5 +-
 src/client/views/nodes/FilterBox.tsx               | 11 ++--
 src/client/views/nodes/button/FontIconBox.tsx      | 68 ++++++++++------------
 .../views/nodes/formattedText/RichTextMenu.tsx     | 20 +++----
 13 files changed, 81 insertions(+), 95 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 8435825c9..60bb375c8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1616,7 +1616,7 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
     return [];
 });
 Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
-    let selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
     if (selected && checkParent) {
         const parentDoc: Doc = Cast(selected.context, Doc, null);
         selected = parentDoc;
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 635673025..e910a9118 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -29,7 +29,7 @@ export namespace Hypothesis {
      * Search for a WebDocument whose url field matches the given uri, return undefined if not found
      */
     export const findWebDoc = async (uri: string) => {
-        const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
+        const currentDoc = SelectionManager.Docs().lastElement();
         if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
 
         const results: Doc[] = [];
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0cfaebbf2..bac13373c 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -11,7 +11,7 @@ export namespace SelectionManager {
     class Manager {
 
         @observable IsDragging: boolean = false;
-        SelectedViews: ObservableMap<DocumentView, boolean> = new ObservableMap();
+        SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
         @observable SelectedSchemaDocument: Doc | undefined;
         @observable SelectedSchemaCollection: CollectionSchemaView | undefined;
 
@@ -28,19 +28,18 @@ export namespace SelectionManager {
                     this.DeselectAll();
                 }
 
-                manager.SelectedViews.set(docView, true);
+                manager.SelectedViews.set(docView, docView.rootDoc);
                 docView.props.whenChildContentsActiveChanged(true);
             } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) {
                 Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
                 manager.SelectedSchemaDocument = undefined;
                 manager.SelectedSchemaCollection = undefined;
                 manager.SelectedViews.clear();
-                manager.SelectedViews.set(docView, true);
+                manager.SelectedViews.set(docView, docView.rootDoc);
             }
         }
         @action
         DeselectView(docView: DocumentView): void {
-
             if (manager.SelectedViews.get(docView)) {
                 manager.SelectedViews.delete(docView);
                 docView.props.whenChildContentsActiveChanged(false);
@@ -92,7 +91,7 @@ export namespace SelectionManager {
     }
 
     export function Views(): Array<DocumentView> {
-        return Array.from(manager.SelectedViews.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
+        return Array.from(manager.SelectedViews.keys()).filter(dv => manager.SelectedViews.get(dv)?._viewType !== CollectionViewType.Docking);
     }
     export function SelectedSchemaDoc(): Doc | undefined {
         return manager.SelectedSchemaDocument;
@@ -100,4 +99,7 @@ export namespace SelectionManager {
     export function SelectedSchemaCollection(): CollectionSchemaView | undefined {
         return manager.SelectedSchemaCollection;
     }
+    export function Docs(): Doc[] {
+        return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 0127d3080..f66c9c788 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -229,8 +229,8 @@ export class KeyManager {
                 }
                 break;
             case "o":
-                const target = SelectionManager.Views()[0];
-                target && CollectionDockingView.OpenFullScreen(target.props.Document);
+                const target = SelectionManager.Docs().lastElement();
+                target && CollectionDockingView.OpenFullScreen(target);
                 break;
             case "r":
                 preventDefault = false;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 09e12443b..ab9022a84 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -366,7 +366,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
             <div className="expansion-button" onPointerDown={() => {
                 if (this.selectedDocumentView || this.selectedDoc) {
-                    SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc);
+                    SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
                 }
             }}>
                 <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 3413179ed..3c88a4830 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -96,7 +96,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
             StrCast(doc._showTitle,
                 props?.showTitle?.() ||
-                (!Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
+                (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
                     (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
                         "author;creationDate") : "")) || "");
         case StyleProp.Color:
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index ce5846502..aefa1ec3d 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -62,7 +62,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     componentDidMount() {
         reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
-            (doc) => doc && this.SetSelection(doc));
+            view => view && this.SetSelection(view));
     }
 
     @action
@@ -441,10 +441,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
         </div>);
     }
 
-    @computed get selectedDocumentView() {
-        return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    }
-    @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
     @computed get notACollection() {
         if (this.selectedDoc) {
             const layoutField = Doc.LayoutField(this.selectedDoc);
@@ -552,8 +550,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     @undoBatch
     onAliasButtonMoved = (e: PointerEvent) => {
         const contentDiv = this.selectedDocumentView?.ContentDiv;
-        if (contentDiv) {
-            const dragData = new DragManager.DocumentDragData([this.selectedDocumentView!.props.Document]);
+        if (contentDiv && this.selectedDoc) {
+            const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
             const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
             dragData.defaultDropAction = "alias";
             dragData.canEmbed = true;
@@ -648,11 +646,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
         return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
     }
     @computed get childDocs() { return DocListCast(this.dataField); }
-    @computed get selectedDocumentView() { return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; }
-    @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
-    @computed get isText() {
-        return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
-    }
+    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
+    @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }
 
     @undoBatch
     @action
@@ -826,7 +822,6 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
 
             </div>;
     }
-    @observable viewType = this.selectedDoc?._viewType;
 
     render() {
         return !this.props.docView.layoutDoc ? (null) :
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b70df93da..06d20f015 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -321,12 +321,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                                 }
                             });
                         } else {
-                            let srcUrl: string | undefined;
-                            let srcWeb: Doc | undefined;
-                            if (SelectionManager.Views().length) {
-                                srcWeb = SelectionManager.Views()[0].props.Document;
-                                srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
-                            }
+                            const srcWeb = SelectionManager.Docs().lastElement();
+                            const srcUrl = (srcWeb?.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
                             const reg = new RegExp(Utils.prepend(""), "g");
                             const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
                             const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index fae92e076..7fe95fef0 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,24 +1,24 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, HeightSym, WidthSym, Opt } from '../../../../fields/Doc';
+import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
+import { Colors, Shadows } from '../../global/globalEnums';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
 import { StyleProp } from '../../StyleProvider';
-import "./CollectionLinearView.scss";
 import { CollectionSubView } from '../CollectionSubView';
 import { CollectionViewType } from '../CollectionView';
-import { Colors, Shadows } from '../../global/globalEnums';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import "./CollectionLinearView.scss";
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -115,9 +115,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     }
 
 
-    getDisplayDoc(doc: Doc) {
+    getDisplayDoc = (doc: Doc) => {
         const nested = doc._viewType === CollectionViewType.Linear;
-        const hidden = doc.hidden === true;;
+        const hidden = doc.hidden === true;
 
         let dref: Opt<HTMLDivElement>;
         const docXf = () => this.getTransform(dref);
@@ -142,8 +142,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                 pinToPres={emptyFunction}
                 rootSelected={this.props.isSelected}
                 removeDocument={this.props.removeDocument}
-                // ScreenToLocalTransform={docXf} // bcz: Ugh -- this is what we want, but it breaks things when right-clicking on linear view item since docXf is not a constant value across invalidations
-                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                ScreenToLocalTransform={docXf}
                 PanelWidth={nested ? doc[WidthSym] : this.dimension}
                 PanelHeight={nested ? doc[HeightSym] : this.dimension}
                 renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6f97cdbd8..8b19fb204 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -649,7 +649,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
             e.preventDefault();
             e.stopPropagation();
-            !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
+            //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
         }
         // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
         if (e) {
@@ -952,11 +952,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     hideOnLeave={true}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
+                    isContentActive={this.isContentActive}
                     onClick={this.onClickFunc}
                 />
             </div>;
         const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
-        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
+        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
         const titleView = !showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: this.headerMargin ? "relative" : "absolute",
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 7ad03e055..e9f19bf9e 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../fields/List";
 import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec, makeInterface } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -79,7 +79,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     */
     @computed static get targetDoc() {
-        return SelectionManager.Views().length ? SelectionManager.Views()[0].Document : CurrentUserUtils.ActiveDashboard;
+        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : CurrentUserUtils.ActiveDashboard;
     }
     @computed static get targetDocChildKey() {
         if (SelectionManager.Views().length) {
@@ -88,10 +88,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         return "data";
     }
     @computed static get targetDocChildren() {
-        if (SelectionManager.Views().length) {
-            return DocListCast(FilterBox.targetDoc[FilterBox.targetDocChildKey]);
-        }
-        return DocListCast(CurrentUserUtils.ActiveDashboard.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data);
     }
 
     @observable _loaded = false;
@@ -347,7 +344,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         );
     }
     setTreeHeight = (hgt: number) => {
-        this.layoutDoc._height = hgt + 140; // 50?  need to add all the border sizes together.
+        this.layoutDoc._height = NumCast(this.layoutDoc._autoHeightMargins) + 150; // need to add all the border sizes together.
     }
 
     /**
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 4475cf347..df7c54f67 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -248,14 +248,15 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         let noneSelected: boolean = false;
 
         if (script === 'setView') {
-            const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-            if (selected && StrCast(selected.Document.type) === DocumentType.COL) {
-                text = StrCast(selected.Document._viewType);
-            } else if (selected) {
-                dropdown = false;
-                text = StrCast(selected.Document.type);
-                if (text === DocumentType.RTF) text = "Text";
-                icon = Doc.toIcon(selected.Document);
+            const selected = SelectionManager.Docs().lastElement();
+            if (selected) {
+                if (StrCast(selected.type) === DocumentType.COL) {
+                    text = StrCast(selected._viewType);
+                } else {
+                    dropdown = false;
+                    text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+                    icon = Doc.toIcon(selected);
+                }
             } else {
                 dropdown = false;
                 icon = "globe-asia";
@@ -263,13 +264,8 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
         } else if (script === 'setFont') {
-            const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-            if (selected && StrCast(selected.Document.type) === DocumentType.RTF) {
-                text = StrCast(selected.Document._fontFamily);
-            } else {
-                const fontFamily = StrCast(Doc.UserDoc()._fontFamily);
-                text = fontFamily;
-            }
+            const selected = SelectionManager.Docs().lastElement();
+            text = StrCast((selected?.type === DocumentType.RTF ? selected : Doc.UserDoc())._fontFamily);
             noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
                 "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
         }
@@ -584,13 +580,13 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setView(view: string) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
+    const selected = SelectionManager.Docs().lastElement();
+    selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    const selected = SelectionManager.Docs().lastElement();
     if (checkResult) {
         if (selected) {
             console.log("[Background] (selected): " + StrCast(selected._backgroundColor));
@@ -635,7 +631,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
-    SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font);
+    SelectionManager.Docs().map(doc => doc._fontFamily = font);
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView);
     Doc.UserDoc()._fontFamily = font;
@@ -665,7 +661,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
         if (active === align) return Colors.MEDIUM_BLUE;
         else return "transparent";
     }
-    SelectionManager.Views().map(dv => dv.props.Document.textAlign = align);
+    SelectionManager.Docs().map(doc => doc.textAlign = align);
     switch (align) {
         case "left":
             editorView?.state && RichTextMenu.Instance.alignLeft(editorView, editorView.dispatch);
@@ -703,13 +699,13 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    const selected = SelectionManager.Docs().lastElement();
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
 
     if (checkResult) {
         if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected.rootDoc._fontColor));
-            return selected.rootDoc._fontColor;
+            console.log("[Font color] (selected): " + StrCast(selected._fontColor));
+            return selected._fontColor;
         } else {
             console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
             return Doc.UserDoc()._fontColor;
@@ -717,7 +713,7 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
     }
 
     if (selected) {
-        selected.rootDoc._fontColor = color;
+        selected._fontColor = color;
         if (color) {
             editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
         }
@@ -727,18 +723,18 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    const selected = SelectionManager.Docs().lastElement();
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
 
     if (checkResult) {
         if (selected) {
-            return selected.Document._fontHighlight;
+            return selected._fontHighlight;
         } else {
             return Doc.UserDoc()._fontHighlight;
         }
     }
     if (selected) {
-        selected.rootDoc._fontColor = color;
+        selected._fontColor = color;
         if (color) {
             editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
         }
@@ -769,7 +765,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
         console.log("editorView");
         editorView?.state && RichTextMenu.Instance.toggleBold(editorView, true);
     }
-    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.bold = !dv.props.Document.bold);
+    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.bold = !doc.bold);
     Doc.UserDoc().bold = !Doc.UserDoc().bold;
     return Doc.UserDoc().bold;
 });
@@ -784,7 +780,7 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
         console.log("editorView");
         editorView?.state && RichTextMenu.Instance.toggleUnderline(editorView, true);
     }
-    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.underline = !dv.props.Document.underline);
+    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.underline = !doc.underline);
     Doc.UserDoc().underline = !Doc.UserDoc().underline;
     return Doc.UserDoc().underline;
 });
@@ -799,7 +795,7 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
         console.log("editorView");
         editorView?.state && RichTextMenu.Instance.toggleItalic(editorView, true);
     }
-    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.italic = !doc.italic);
     Doc.UserDoc().italic = !Doc.UserDoc().italic;
     return Doc.UserDoc().italic;
 });
@@ -841,7 +837,7 @@ Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolea
 
 Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
     if (checkResult) {
-        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        const selected = SelectionManager.Docs().lastElement();
         if (selected && selected.type === DocumentType.INK) {
             return Number(selected.strokeWidth);
         } else {
@@ -850,13 +846,13 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean
         }
     }
     Doc.UserDoc().activeInkWidth = width;
-    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width));
+    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.INK).map(doc => doc.strokeWidth = Number(width));
 });
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
-        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        const selected = SelectionManager.Docs().lastElement();
         if (selected && selected.type === DocumentType.INK) {
             return selected.color;
         } else {
@@ -865,7 +861,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
         }
     }
     SetActiveInkColor(StrCast(color));
-    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.color = String(color));
+    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.INK).map(doc => doc.color = String(color));
 });
 
 
@@ -873,7 +869,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
  *      webSetURL
  **/
 Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    const selected = SelectionManager.Docs().lastElement();
     console.log("URL: ", url);
     if (checkResult && selected && selected.type === DocumentType.WEB) {
         return Cast(selected.data, WebField, null).url;
@@ -888,7 +884,7 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
  *      toggleSchemaPreview
  **/
 Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    const selected = SelectionManager.Docs().lastElement();
     console.log(selected && selected.title);
     if (checkResult && selected) {
         const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 3d9d9543d..3919fbf94 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -279,33 +279,33 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         });
     }
 
-    toggleBold = (view: EditorView, forceBool?:boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.strong, {strong: forceBool});
+    toggleBold = (view: EditorView, forceBool?: boolean) => {
+        const mark = view.state.schema.mark(view.state.schema.marks.strong, { strong: forceBool });
         this.setMark(mark, view.state, view.dispatch, false);
         view.focus();
     }
 
-    toggleUnderline = (view: EditorView, forceBool?:boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.underline, {underline: forceBool});
+    toggleUnderline = (view: EditorView, forceBool?: boolean) => {
+        const mark = view.state.schema.mark(view.state.schema.marks.underline, { underline: forceBool });
         this.setMark(mark, view.state, view.dispatch, false);
         view.focus();
     }
 
-    toggleItalic = (view: EditorView, forceBool?:boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.em, {em: forceBool});
+    toggleItalic = (view: EditorView, forceBool?: boolean) => {
+        const mark = view.state.schema.mark(view.state.schema.marks.em, { em: forceBool });
         this.setMark(mark, view.state, view.dispatch, false);
         view.focus();
     }
 
 
-    setFontSize = (size:number, view: EditorView) => {
+    setFontSize = (size: number, view: EditorView) => {
         const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size });
         this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
         view.focus();
         this.updateMenu(view, undefined, this.props);
     }
 
-    setFontFamily = (family:string, view: EditorView) => {
+    setFontFamily = (family: string, view: EditorView) => {
         const fmark = view.state.schema.marks.pFontFamily.create({ family: family });
         this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
         view.focus();
@@ -516,7 +516,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
     get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
 
-    
+
 
     @action setActiveHighlight(color: string) { this.activeHighlightColor = color; }
 
@@ -566,7 +566,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                         if (linkDoc instanceof Doc) {
                             const anchor1 = await Cast(linkDoc.anchor1, Doc);
                             const anchor2 = await Cast(linkDoc.anchor2, Doc);
-                            const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
+                            const currentDoc = SelectionManager.Docs().lastElement();
                             if (currentDoc && anchor1 && anchor2) {
                                 if (Doc.AreProtosEqual(currentDoc, anchor1)) {
                                     return StrCast(anchor2.title);
-- 
cgit v1.2.3-70-g09d2


From c9c9d00b581bc7f80edbf7848a8ffb3a8e64f005 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 10 Sep 2021 10:14:04 -0400
Subject: event handling fixes for ink.  double click on deselected ink takes
 shows handles.  lightbox view works.  transparent fills are not selectable.

---
 src/client/util/InteractionUtils.tsx               |  2 +-
 src/client/views/InkingStroke.tsx                  | 48 ++++++++++++++++------
 src/client/views/nodes/DocumentContentsView.tsx    |  1 -
 src/client/views/nodes/DocumentView.tsx            |  8 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 5 files changed, 41 insertions(+), 21 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index e009fb3a9..8429a806a 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -209,7 +209,7 @@ export namespace InteractionUtils {
                 points={strpts}
                 style={{
                     // filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
-                    fill: fill ? fill : "none",
+                    fill: fill && fill !== "transparent" ? fill : "none",
                     opacity: 1.0,
                     // opacity: strokeWidth !== width ? 0.5 : undefined,
                     pointerEvents: pevents as any,
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index db09849fd..dc2a5b39b 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,26 +1,26 @@
 import React = require("react");
-import { action, observable } from "mobx";
+import { action, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../fields/Doc";
 import { documentSchema } from "../../fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { makeInterface } from "../../fields/Schema";
-import { Cast, StrCast, NumCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { TraceMobx } from "../../fields/util";
-import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
+import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { InteractionUtils } from "../util/InteractionUtils";
 import { Scripting } from "../util/Scripting";
 import { ContextMenu } from "./ContextMenu";
 import { ViewBoxBaseComponent } from "./DocComponent";
-import "./InkStroke.scss";
-import { FieldView, FieldViewProps } from "./nodes/FieldView";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { GestureOverlay } from "./GestureOverlay";
+import { Colors } from "./global/globalEnums";
 import { InkControls } from "./InkControls";
 import { InkHandles } from "./InkHandles";
-import { Colors } from "./global/globalEnums";
-import { GestureOverlay } from "./GestureOverlay";
+import "./InkStroke.scss";
+import { InkStrokeProperties } from "./InkStrokeProperties";
+import { FieldView, FieldViewProps } from "./nodes/FieldView";
 
 type InkDocument = makeInterface<[typeof documentSchema]>;
 const InkDocument = makeInterface(documentSchema);
@@ -29,6 +29,8 @@ const InkDocument = makeInterface(documentSchema);
 export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
     static readonly MaskDim = 50000;
     @observable private _properties?: InkStrokeProperties;
+    _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated
+    _selDisposer: IReactionDisposer | undefined;
 
     constructor(props: FieldViewProps & InkDocument) {
         super(props);
@@ -36,6 +38,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         this._properties = InkStrokeProperties.Instance;
     }
 
+    componentDidMount() {
+        this._selDisposer = reaction(() => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
+            selected => !selected && this.toggleControlButton());
+    }
+    componentWillUnmount() {
+        this._selDisposer?.();
+    }
+
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(InkingStroke, fieldStr);
     }
@@ -53,14 +63,26 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined;
     });
 
+    onClick = (e: React.MouseEvent) => {
+        if (this._handledClick) {
+            e.stopPropagation(); //stop the event so that docView won't open the lightbox
+        }
+    }
+
     /**
      * Handles the movement of the entire ink object when the user clicks and drags.
      */
     onPointerDown = (e: React.PointerEvent) => {
+        this._handledClick = false;
         if (this.props.isSelected(true)) {
             setupMoveUpEvents(this, e, returnFalse, emptyFunction,
-                action((e: PointerEvent, doubleTap: boolean | undefined) =>
-                    doubleTap && this._properties && (this._properties._controlButton = true))
+                action((e: PointerEvent, doubleTap: boolean | undefined) => {
+                    doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
+                    if (doubleTap && this._properties) {
+                        this._properties._controlButton = true;
+                        this._handledClick = true;  // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
+                    }
+                }), this._properties?._controlButton, this._properties?._controlButton
             );
         }
     }
@@ -77,7 +99,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
 
     render() {
         TraceMobx();
-        this.toggleControlButton();
         // Extracting the ink data and formatting information of the current ink stroke.
         // console.log(InkingStroke.InkShape);
         const InkShape = GestureOverlay.Instance.InkShape;
@@ -115,12 +136,13 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         return (
             <svg className="inkStroke"
                 style={{
-                    pointerEvents: this.props.Document.isInkMask && this.props.layerProvider?.(this.props.Document) !== false ? "all" : "none",
+                    pointerEvents: "none",
                     transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
                     mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
                     overflow: "visible",
                 }}
                 onPointerDown={this.onPointerDown}
+                onClick={this.onClick}
                 onContextMenu={() => {
                     const cm = ContextMenu.Instance;
                     if (cm) {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index fad905d6d..dbab5e762 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -113,7 +113,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
     scaling?: () => number,
     setHeight: (height: number) => void,
     layoutKey: string,
-    hideOnLeave?: boolean,
 }> {
     @computed get layout(): string {
         TraceMobx();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8b19fb204..a2d2f17b6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -808,7 +808,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any) || (this.isContentActive() ? "all" : "none"),
+                pointerEvents: this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none",
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
@@ -943,13 +943,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const showTitleHover = this.ShowTitle?.includes(":hover");
         const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
         const captionView = !showCaption ? (null) :
-            <div className="documentView-captionWrapper">
+            <div className="documentView-captionWrapper"
+                style={{ pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined, }}>
                 <FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
                     yPadding={10}
                     xPadding={10}
                     fieldKey={showCaption}
                     fontSize={Math.min(32, 12 * this.props.ScreenToLocalTransform().Scale)}
-                    hideOnLeave={true}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
                     isContentActive={this.isContentActive}
@@ -964,7 +964,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 height: this.titleHeight,
                 color: lightOrDark(background),
                 background,
-                pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
+                pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
             }}>
                 <EditableView ref={this._titleRef}
                     contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 78de1fd89..e7a44f113 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -69,7 +69,6 @@ const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
     makeLink?: () => Opt<Doc>;  // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
-    hideOnLeave?: boolean;  // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
     xPadding?: number;   // used to override document's settings for xMargin --- see CollectionCarouselView
     yPadding?: number;
     noSidebar?: boolean;
@@ -1558,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         TraceMobx();
         const selected = this.props.isSelected();
         const active = this.props.isContentActive();
-        const scale = this.props.hideOnLeave ? 1 : (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
-- 
cgit v1.2.3-70-g09d2

