From 9f24c29df130ce4e206bc655ed7025e58c1517ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 19 Aug 2021 02:22:38 -0400
Subject: changed how treeview cycles between options for expanded views to
 show 'data' and some more adjustments.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index fa7e75202..d09d9b9d7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1210,7 +1210,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (this.props.Document.annotationOn) {
             return this.rootDoc;
         }
-        const anchor = Docs.Create.TextanchorDocument({ title: StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
+        const anchor = Docs.Create.TextanchorDocument({ title: "ViewSpec - " + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
         const proto = Doc.GetProto(anchor);
         proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
         proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 fe818efd80e649b841009b69841b176cee4c5c8c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 25 Aug 2021 11:53:49 -0400
Subject: cleaned up and fixed tests for nonrecursive doc filters as used in
 PDF/Web to separate transparent from non-transparent docs into layers.

---
 src/Utils.ts                                       | 12 +++++-
 src/client/views/collections/CollectionSubView.tsx | 48 ++++++++++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 src/client/views/nodes/WebBox.tsx                  | 14 ++++---
 src/client/views/pdf/PDFViewer.tsx                 | 14 ++++---
 src/fields/Doc.ts                                  |  2 +-
 6 files changed, 58 insertions(+), 36 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index f90296121..5d86c755a 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -115,13 +115,21 @@ export namespace Utils {
         return { r: r, g: g, b: b, a: a };
     }
 
+    const isTransparentFunctionHack = "isTransparent(__value__)";
+    const noRecursionHack = "__noRecursion";
+    export function IsRecursiveFilter(val: string) {
+        return !val.includes(noRecursionHack);
+    }
+    export function HasTransparencyFilter(val: string) {
+        return val.includes(isTransparentFunctionHack);
+    }
     export function IsTransparentFilter() {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
-        return ["backgroundColor:isTransparent(__value__):check"];
+        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:check`;// bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
     export function IsOpaqueFilter() {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
-        return ["backgroundColor:isTransparent(__value__):x"];
+        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`;// bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
 
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 55b1e4031..1f4c35daa 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -90,10 +90,11 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         }
         collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
         collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
-        childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
+        childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
+        unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
         IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters().filter(f => !f.includes("__value__")).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
@@ -113,10 +114,10 @@ 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.childDocFilters();
+            const childDocFilters = this.childDocFilters();
             const docRangeFilters = this.childDocRangeFilters();
             const searchDocs = this.searchFilterDocs();
-            if (this.props.Document.dontRegisterView || (!docFilters.length && !docRangeFilters.length && !searchDocs.length)) {
+            if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().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
             }
 
@@ -127,24 +128,27 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const docsforFilter: Doc[] = [];
             childDocs.forEach((d) => {
                 // if (DocUtils.Excluded(d, docFilters)) return;
-                let notFiltered = d.z || Doc.IsSystem(d) || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
-                const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                if (data !== undefined) {
-                    let subDocs = DocListCast(data);
-                    if (subDocs.length > 0) {
-                        let newarray: Doc[] = [];
-                        notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript, d).length);
-                        while (subDocs.length > 0 && !notFiltered) {
-                            newarray = [];
-                            subDocs.forEach((t) => {
-                                const fieldKey = Doc.LayoutFieldKey(t);
-                                const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                                notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript, d).length));
-                                DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
-                            });
-                            subDocs = newarray;
+                let notFiltered = d.z || Doc.IsSystem(d) || (DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0);
+                if (notFiltered) {
+                    notFiltered = ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
+                    const fieldKey = Doc.LayoutFieldKey(d);
+                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                    if (data !== undefined) {
+                        let subDocs = DocListCast(data);
+                        if (subDocs.length > 0) {
+                            let newarray: Doc[] = [];
+                            notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, viewSpecScript, d).length);
+                            while (subDocs.length > 0 && !notFiltered) {
+                                newarray = [];
+                                subDocs.forEach((t) => {
+                                    const fieldKey = Doc.LayoutFieldKey(t);
+                                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
+                                    notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
+                                    DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+                                });
+                                subDocs = newarray;
+                            }
                         }
                     }
                 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d09d9b9d7..b5d9ebd9f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -74,6 +74,8 @@ export type collectionFreeformViewProps = {
     scaleField?: string;
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
+    dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are trnasparent or not.  
+    // However, this screws up interactions since only the top layer gets events.  so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
 };
 
 @observer
@@ -1452,7 +1454,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             getContainerTransform={this.getContainerTransform}
             getTransform={this.getTransform}
             isAnnotationOverlay={this.isAnnotationOverlay}>
-            <div ref={this._marqueeRef}>
+            <div ref={this._marqueeRef} style={{ display: this.props.dontRenderDocuments ? "none" : undefined }}>
                 {this.layoutDoc["_backgroundGrid-show"] ? this.backgroundGrid : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index cdc79678a..ae6b90e41 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -13,7 +13,7 @@ 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 } from "../../../Utils";
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils, returnEmptyString, returnEmptyFilter } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -508,11 +508,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     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));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     render() {
         const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
-        const renderAnnotations = (docFilters: () => string[]) =>
+        const renderAnnotations = (docFilters?: () => string[]) =>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
@@ -524,7 +526,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 scaling={returnOne}
                 dropAction={"alias"}
-                docFilters={docFilters}
+                docFilters={docFilters || this.props.docFilters}
+                dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
                 isContentActive={returnFalse}
                 ContentScaling={returnOne}
@@ -553,9 +556,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         <div className={"webBox-innerContent"} style={{ height: NumCast(this.scrollHeight, 50), pointerEvents }}>
                             {this.content}
                             <div style={{ mixBlendMode: "multiply" }}>
-                                {renderAnnotations(Utils.IsTransparentFilter)}
+                                {renderAnnotations(this.transparentFilter)}
                             </div>
-                            {renderAnnotations(Utils.IsOpaqueFilter)}
+                            {renderAnnotations(this.opaqueFilter)}
+                            {renderAnnotations()}
                             {this.annotationLayer}
                         </div>
                     </div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0261d24d9..734d9127c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,7 +9,7 @@ import { createSchema } from "../../../fields/Schema";
 import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { PdfField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse } from "../../../Utils";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse, returnEmptyString, returnEmptyFilter } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -506,8 +506,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
     overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (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);
+    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     @computed get overlayLayer() {
-        const renderAnnotations = (docFilters: () => string[]) =>
+        const renderAnnotations = (docFilters?: () => string[]) =>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 isAnnotationOverlay={true}
                 isContentActive={returnFalse}
@@ -519,7 +521,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 select={emptyFunction}
                 ContentScaling={this.contentZoom}
                 bringToFront={emptyFunction}
-                docFilters={docFilters}
+                docFilters={docFilters || this.props.docFilters}
+                dontRenderDocuments={docFilters ? false : true}
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.overlayTransform}
                 renderDepth={this.props.renderDepth + 1}
@@ -531,7 +534,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     mixBlendMode: "multiply",
                     transform: `scale(${this._zoomed})`
                 }}>
-                {renderAnnotations(Utils.IsTransparentFilter)}
+                {renderAnnotations(this.transparentFilter)}
             </div>
             <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
                 style={{
@@ -539,7 +542,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
                     transform: `scale(${this._zoomed})`
                 }}>
-                {renderAnnotations(Utils.IsOpaqueFilter)}
+                {renderAnnotations(this.opaqueFilter)}
+                {renderAnnotations()}
             </div>
         </div>;
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index f6efefdf9..17f41fac8 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1088,7 +1088,7 @@ export namespace Doc {
     }
 
     export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
-        if (value === "isTransparent(__value__)") {
+        if (Utils.HasTransparencyFilter(value)) {
             const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1);
             return isTransparent(StrCast(doc[key]));
         }
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 5bb197f5dad55d15b144e2f86bb2ec94c7a261fd Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Mon, 30 Aug 2021 14:49:33 -0400
Subject: GitHubs are default view

---
 src/client/documents/Documents.ts                                 | 3 +++
 src/client/util/CurrentUserUtils.ts                               | 3 ++-
 src/client/views/PropertiesButtons.tsx                            | 2 +-
 src/client/views/collections/CollectionDockingView.tsx            | 4 ++--
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 .../collections/collectionLinearView/CollectionLinearView.tsx     | 8 ++++----
 src/server/DashUploadUtils.ts                                     | 2 +-
 7 files changed, 14 insertions(+), 10 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ec856e748..9d5dcb217 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -215,6 +215,9 @@ export class DocumentOptions {
     isPushpin?: boolean;
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
 
+    // BACKGROUND GRID
+    _backgroundGridShow?: boolean;
+
     //BUTTONS
     iconShape?: string; // shapes of the fonticon border
     btnType?: string;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index ef24acf6d..bcc47c355 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -416,7 +416,7 @@ export class CurrentUserUtils {
             ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _backgroundGridShow: true, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptySlide === undefined) {
@@ -1465,6 +1465,7 @@ export class CurrentUserUtils {
             _width: 1500,
             _height: 1000,
             _fitWidth: true,
+            _backgroundGridShow: true,
             title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 5c41a96d0..86ef9ab62 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -83,7 +83,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Auto\xA0Size", "_autoHeight", on => `Automatical vertical sizing to show all content`, on => "arrows-alt-v");
     }
     @computed get gridButton() {
-        return this.propertyToggleBtn("Grid", "_backgroundGrid-show", on => `Display background grid in collection`, on => "border-all");
+        return this.propertyToggleBtn("Grid", "_backgroundGridShow", on => `Display background grid in collection`, on => "border-all");
     }
     @computed get snapButton() {
         return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cae08e1f4..5325d5827 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -428,7 +428,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
@@ -453,7 +453,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1c54467c5..dd1f3e707 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1457,7 +1457,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             getTransform={this.getTransform}
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div ref={this._marqueeRef} style={{ display: this.props.dontRenderDocuments ? "none" : undefined }}>
-                {this.layoutDoc["_backgroundGrid-show"] ? this.backgroundGrid : (null)}
+                {this.layoutDoc["_backgroundGridShow"] ? this.backgroundGrid : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
                     transform={this.contentTransform}
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 74d581946..013caecaf 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -18,6 +18,7 @@ import "./CollectionLinearView.scss";
 import { CollectionSubView } from '.././CollectionSubView';
 import { CollectionViewType } from '.././CollectionView';
 import { Colors, Shadows } from '../../global/globalEnums';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -119,14 +120,13 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
         const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
         const menuOpener = <label htmlFor={`${guid}`}
             style={{
+                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
                 color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
                 backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
             }}
             onPointerDown={e => e.stopPropagation()} >
-            <div className="collectionLinearView-menuOpener"
-                style={{ boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined }}
-            >
-                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : "–" : icon ? icon : "+"}
+            <div className="collectionLinearView-menuOpener">
+                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"plus"}/> : icon ? icon :  <FontAwesomeIcon icon={"minus"}/>}
             </div>
         </label>;
 
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 6f2b6e373..c32790df3 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -56,7 +56,7 @@ export namespace DashUploadUtils {
     const size = "content-length";
     const type = "content-type";
 
-    const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia;
+    const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr ASK BOB ABOUT THIS!
 
     export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
         console.log("UPLOAD " + videoId);
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 64ff849d21273f7440917ef15a4b1c7f1909687c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 3 Sep 2021 13:16:48 -0400
Subject: fixed warnings.  cleaned up link lines a bit more - simplified, and
 allowed multiple aliases to share a link.  fixed link lines to text anchors
 from flapping around wildly when camera pans

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                | 12 +++----
 src/client/util/DocumentManager.ts                 | 38 +++++++++++++---------
 src/client/views/PropertiesButtons.tsx             |  2 +-
 .../CollectionFreeFormLinkView.tsx                 | 13 +++++---
 .../CollectionFreeFormLinksView.tsx                | 19 ++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/PDFBox.tsx                  | 10 +++---
 src/client/views/nodes/WebBox.tsx                  |  4 +--
 .../views/nodes/button/textButton/TextButton.tsx   |  2 +-
 src/client/views/nodes/button/textButton/index.ts  |  2 +-
 .../views/nodes/button/toggleButton/index.ts       |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 13 +++-----
 src/client/views/pdf/PDFViewer.tsx                 |  5 ++-
 15 files changed, 60 insertions(+), 67 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 671e27408..9a45da440 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1217,7 +1217,7 @@ export namespace DocUtils {
             }),
             icon: Doc.toIcon(dragDoc),
         })) as ContextMenuProps[];
-        documentList.push(math)
+        documentList.push(math);
         ContextMenu.Instance.addItem({
             description: "Create document",
             subitems: documentList,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 56807c63b..f9c55da29 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -832,7 +832,7 @@ export class CurrentUserUtils {
         await doc.myTrails;
         if (doc.myTrails === undefined) {
             const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
-            const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
+            const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Trails", _showTitle: "title", _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -852,16 +852,16 @@ export class CurrentUserUtils {
             doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
             // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
             const newFolder = ScriptField.MakeFunction(`doc.makeFolder()`, { doc: doc.myFilesystem })!;
-            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            const newFolderButton: Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
             doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title",  buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
                 explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
-            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder!]);
+            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder]);
             (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
         }
         return doc.myFilesystem as any as Doc;
@@ -870,7 +870,7 @@ export class CurrentUserUtils {
     static setupRecentlyClosedDocs(doc: Doc) {
         if (doc.myRecentlyClosedDocs === undefined) {
             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 });
+            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",
@@ -1215,7 +1215,7 @@ export class CurrentUserUtils {
     // Import sidebar is where shared documents are contained
     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 });
+            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,
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b72a3189d..9e190ad02 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,19 +1,21 @@
 import { action, observable, runInAction } from 'mobx';
 import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast } from '../../fields/Types';
 import { returnFalse } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
+import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
 import { Scripting } from './Scripting';
 
 export class DocumentManager {
 
     //global holds all of the nodes (regardless of which collection they're in)
     @observable public DocumentViews: DocumentView[] = [];
+    @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
     @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
 
@@ -26,23 +28,24 @@ export class DocumentManager {
     @action
     public AddView = (view: DocumentView) => {
         //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
-        if (view.props.LayoutTemplateString?.includes("LinkAnchorBox")) {
+        if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
             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
-                            });
-                    });
+            DocListCast(view.rootDoc.links).forEach(link => {
+                this.LinkAnchorBoxViews?.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.LinkAnchorBoxViews.push(view);
             // 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));
+        } else {
+            this.DocumentViews.push(view);
         }
-        this.DocumentViews.push(view);
     }
     public RemoveView = action((view: DocumentView) => {
         this.LinkedDocumentViews.slice().forEach(action(pair => {
@@ -52,8 +55,13 @@ export class DocumentManager {
             }
         }));
 
-        const index = this.DocumentViews.indexOf(view);
-        index !== -1 && this.DocumentViews.splice(index, 1);
+        if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+            const index = this.LinkAnchorBoxViews.indexOf(view);
+            this.LinkAnchorBoxViews.splice(index, 1);
+        } else {
+            const index = this.DocumentViews.indexOf(view);
+            index !== -1 && this.DocumentViews.splice(index, 1);
+        }
     });
 
     //gets all views
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 9fe2122e6..dd737dc9d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -156,7 +156,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             ["toggleDetail", "Toggle Detail"],
             ["linkInPlace", "Follow Link"],
             ["linkOnRight", "Open Link on Right"]
-        ]
+        ];
         const currentSelection = this.selectedDoc.onClickBehavior;
         // Get items to place into the list
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 6fe46bbb2..3b3e069d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -5,7 +5,6 @@ import { Id } from "../../../../fields/FieldSymbols";
 import { List } from "../../../../fields/List";
 import { NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { DocumentType } from "../../../documents/DocumentTypes";
 import { LinkManager } from "../../../util/LinkManager";
 import { SnappingManager } from "../../../util/SnappingManager";
 import { DocumentView } from "../../nodes/DocumentView";
@@ -69,8 +68,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         } else {
             const m = targetAhyperlink.getBoundingClientRect();
             const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
-            linkDoc.anchor1_x = Math.min(1, mp[0] / A.props.PanelWidth()) * 100;
-            linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100;
+            const mpx = mp[0] / A.props.PanelWidth();
+            const mpy = mp[1] / A.props.PanelHeight();
+            if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
         }
         if (!targetBhyperlink) {
             if (linkDoc.linkAutoMove) {
@@ -80,8 +81,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         } else {
             const m = targetBhyperlink.getBoundingClientRect();
             const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
-            linkDoc.anchor2_x = Math.min(1, mp[0] / B.props.PanelWidth()) * 100;
-            linkDoc.anchor2_y = Math.min(1, mp[1] / B.props.PanelHeight()) * 100;
+            const mpx = mp[0] / B.props.PanelWidth();
+            const mpy = mp[1] / B.props.PanelHeight();
+            if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index e812064b7..dacbb3508 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,30 +1,17 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
-import { Utils } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
-import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { LinkManager } from "../../../util/LinkManager";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component {
     @computed get uniqueConnections() {
-        const connections = DocumentManager.Instance.LinkedDocumentViews
-            .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK)
-            .reduce((drawnPairs, connection) => {
-                const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b);
-                matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l));
-                if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set<Doc>([connection.l]) });
-                return drawnPairs;
-            }, [] as { a: DocumentView, b: DocumentView, l: Set<Doc> }[]);
-        const set = new Map<Doc, { a: DocumentView, b: DocumentView, l: Doc[] }>();
-        connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) }));
-        return Array.from(set.values()).map(c => <CollectionFreeFormLinkView key={c.l[0][Id]} A={c.a} B={c.b} LinkDocs={c.l} />);
+        return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
+            <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
+        );
     }
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2deaea823..018c2a895 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1457,7 +1457,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             getTransform={this.getTransform}
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div ref={this._marqueeRef} style={{ display: this.props.dontRenderDocuments ? "none" : undefined }}>
-                {this.layoutDoc["_backgroundGridShow"] ? this.backgroundGrid : (null)}
+                {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
                     transform={this.contentTransform}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 8f9959693..1e0172d24 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -3,7 +3,6 @@ import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from "../../../fields/FieldSymbols";
 import { makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2bafb3b7f..9706d73c7 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,13 +3,13 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from '../../../fields/documentSchemas';
 import { makeInterface } from "../../../fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast } from '../../../fields/Types';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { PdfField } from "../../../fields/URLField";
 import { TraceMobx } from '../../../fields/util';
-import { Utils, setupMoveUpEvents, emptyFunction, returnOne } from '../../../Utils';
+import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch } from '../../util/UndoManager';
@@ -17,14 +17,14 @@ import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeF
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { Colors } from '../global/globalEnums';
+import { AnchorMenu } from '../pdf/AnchorMenu';
 import { PDFViewer } from "../pdf/PDFViewer";
 import { SidebarAnnos } from '../SidebarAnnos';
 import { FieldView, FieldViewProps } from './FieldView';
 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);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index eaedd48c7..7e46d8433 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, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
@@ -24,6 +24,7 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
+import { Colors } from "../global/globalEnums";
 import { LightboxView } from "../LightboxView";
 import { MarqueeAnnotator } from "../MarqueeAnnotator";
 import { AnchorMenu } from "../pdf/AnchorMenu";
@@ -33,7 +34,6 @@ 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");
 
diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx
index 414b50dcb..e18590a95 100644
--- a/src/client/views/nodes/button/textButton/TextButton.tsx
+++ b/src/client/views/nodes/button/textButton/TextButton.tsx
@@ -12,6 +12,6 @@ export class TextButton extends Component<IButtonProps> {
         return (<div className={`menuButton ${this.props.type}`} style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}>
             <FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
             {this.props.label}
-        </div>)
+        </div>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts
index 01fcde54f..01d62eb7e 100644
--- a/src/client/views/nodes/button/textButton/index.ts
+++ b/src/client/views/nodes/button/textButton/index.ts
@@ -1 +1 @@
-export * from './TextButton'
\ No newline at end of file
+export * from './TextButton';
\ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts
index 48b28fb4c..cdb9c527c 100644
--- a/src/client/views/nodes/button/toggleButton/index.ts
+++ b/src/client/views/nodes/button/toggleButton/index.ts
@@ -1 +1 @@
-export * from './ToggleButton'
\ No newline at end of file
+export * from './ToggleButton';
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bc578f95d..cad9af2b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1448,18 +1448,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
             const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
             if (children) {
-                var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
-                var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+                const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+                const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
                 if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                     const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
                     if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                         setScrollHeight();
-                        // setTimeout(() => {
-                        //     proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
-                        //     scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-                        //     scrollHeight && setScrollHeight();
-                        // }, 10);
-                    } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
+                    } else {
+                        setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
+                    }
                 }
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 02010e123..d953c6b6c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,14 +2,13 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { InkTool } from "../../../fields/InkField";
-import { createSchema } from "../../../fields/Schema";
 import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { PdfField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse, returnEmptyString, returnEmptyFilter } from "../../../Utils";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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

