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/views/nodes/formattedText/FormattedTextBox.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

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/nodes/formattedText/FormattedTextBox.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 a11cbed94999a437d3b51dbf8632441acdb8b5be Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Sep 2021 09:33:16 -0400
Subject: tried adjusting text annotation button so text would be more readable
 in corner.

---
 .../views/nodes/formattedText/FormattedTextBox.scss     | 17 +++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx      |  6 ++++--
 2 files changed, 11 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 56f5c5631..4134e3c67 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -66,11 +66,12 @@ audiotag:hover {
     
 .formattedTextBox-sidebar-handle {
     position: absolute;
-    top: 5px;
+    top: 0;
+    left: 17px;
     //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
-    width: 25px;
-    height: 100%;
-    max-height: 25px;
+    width: 17px;
+    height: 17px;
+    border-radius: 3px;
     color: white;
     background: $medium-gray;
     border-radius: 5px;
@@ -80,8 +81,9 @@ audiotag:hover {
     cursor:grabbing;
     box-shadow: $standard-box-shadow;
     // transition: 0.2s;
-
+    opacity: 0.3;
     &:hover{
+        opacity: 1 !important;
         filter: brightness(0.85);
     }
 
@@ -425,12 +427,7 @@ footnote::after {
     
     .formattedTextBox-sidebar-handle {
         position: absolute;
-        top: 0;
-        //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
-        width: 10px;
-        height: 35px;
         background: lightgray;
-        border-radius: 20px;
         cursor: grabbing;
     }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index cad9af2b0..bc19320e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1360,6 +1360,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this._undoTyping = undefined;
         return wasUndoing;
     }
+
     @action
     onBlur = (e: any) => {
         FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
@@ -1485,9 +1486,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
         return (!annotated && !this.props.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
             style={{
-                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 30px"}))`,
+                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 17px"}))`,
                 backgroundColor: backgroundColor,
-                color: color
+                color: color,
+                opacity: annotated ? 1 : undefined
             }} >
             <FontAwesomeIcon icon={"comment-alt"} />
         </div>;
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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

