From d7150995d62c498ab8435de986b90d98bdca020c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 12 Nov 2019 15:07:03 -0500
Subject: a bunch of cleanup and bug fixes to text links and doculink buttons

---
 src/client/views/nodes/FormattedTextBoxComment.tsx | 83 ++++++++++++----------
 1 file changed, 45 insertions(+), 38 deletions(-)

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

diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index bde278be3..2a7467108 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -6,8 +6,9 @@ import { Doc } from "../../../new_fields/Doc";
 import { schema } from "../../util/RichTextSchema";
 import { DocServer } from "../../DocServer";
 import { Utils } from "../../../Utils";
-import { StrCast } from "../../../new_fields/Types";
+import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
 import { FormattedTextBox } from "./FormattedTextBox";
+import { DocUtils } from "../../documents/Documents";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -61,6 +62,7 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
+            FormattedTextBoxComment.tooltip.style.maxWidth = "350px";
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
@@ -91,62 +93,67 @@ export class FormattedTextBoxComment {
         let state = view.state;
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
-            lastState.selection.eq(state.selection)) return;
+            lastState.selection.eq(state.selection)) {
+            return;
+        }
 
-        if (!FormattedTextBoxComment.textBox || !FormattedTextBoxComment.textBox.props || !FormattedTextBoxComment.textBox.props.isSelected()) return;
+        const textBox = FormattedTextBoxComment.textBox;
+        if (!textBox || !textBox.props) {
+            return;
+        }
         let set = "none";
-        if (FormattedTextBoxComment.textBox && state.selection.$from) {
-            let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
+        let nbef = 0;
+        // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
+        if (state.selection.$from) {
+            nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
             let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
-            const spos = state.selection.$from.pos - nbef;
-            const epos = state.selection.$from.pos + naft;
-            let child = state.selection.$from.nodeBefore;
-            let mark = child && findOtherUserMark(child.marks);
             let noselection = view.state.selection.$from === view.state.selection.$to;
+            let child: any = null;
+            state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
+            let mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark);
+                FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
-            if (mark && child && nbef && naft) {
-                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
-                // These are in screen coordinates
-                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-                // The box in which the tooltip is positioned, to use as base
-                let box = (document.getElementById("main-div") as any).getBoundingClientRect();
-                // Find a center-ish x position from the selection endpoints (when
-                // crossing lines, end may be more to the left)
-                let left = Math.max((start.left + end.left) / 2, start.left + 3);
-                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
-                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+            if (mark && child && ((nbef && naft) || !noselection)) {
+                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
                 set = "";
             }
         }
+        // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
         if (set === "none" && state.selection.$from) {
-            FormattedTextBoxComment.textBox = undefined;
-            let nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
+            nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
             let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
-            let child = state.selection.$from.nodeBefore;
+            let child: any = null;
+            state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
-                FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
+                FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                    docTarget && DocServer.GetRefField(docTarget).then(linkDoc =>
-                        (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc).title)));
+                    docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
+                        if (linkDoc instanceof Doc) {
+                            let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
+                            let ext = (target && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
+                            let text = ext && StrCast(ext.text);
+                            ext && (FormattedTextBoxComment.tooltipText.textContent = "=> " + (text || StrCast(ext.title)));
+                        }
+                    });
                 }
-                // These are in screen coordinates
-                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-                // The box in which the tooltip is positioned, to use as base
-                let box = (document.getElementById("main-div") as any).getBoundingClientRect();
-                // Find a center-ish x position from the selection endpoints (when
-                // crossing lines, end may be more to the left)
-                let left = Math.max((start.left + end.left) / 2, start.left + 3);
-                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
-                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
                 set = "";
             }
         }
+        if (set !== "none") {
+            // These are in screen coordinates
+            // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+            let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+            // The box in which the tooltip is positioned, to use as base
+            let box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
+            // Find a center-ish x position from the selection endpoints (when
+            // crossing lines, end may be more to the left)
+            let left = Math.max((start.left + end.left) / 2, start.left + 3);
+            FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+            FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+        }
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
-- 
cgit v1.2.3-70-g09d2


From ecbe527575bab2cb5f1ced278039ec0a6fc50809 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 12 Nov 2019 16:04:05 -0500
Subject: switched text hyper link following to go through pop-up comment box
 now.

---
 src/client/views/nodes/FormattedTextBox.tsx        | 64 +++++++++++-----------
 src/client/views/nodes/FormattedTextBoxComment.tsx | 15 ++++-
 2 files changed, 45 insertions(+), 34 deletions(-)

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

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a0f8523a2..24d6f2509 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -884,38 +884,38 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
     onClick = (e: React.MouseEvent): void => {
         if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); return; }
         (e.nativeEvent as any).formattedHandled = true;
-        if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) {
-            let href = (e.target as any).href;
-            let location: string;
-            if ((e.target as any).attributes.location) {
-                location = (e.target as any).attributes.location.value;
-            }
-            let pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
-            let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos);
-            if (node) {
-                let link = node.marks.find(m => m.type === this._editorView!.state.schema.marks.link);
-                if (link && !(link.attrs.docref && link.attrs.title)) {  // bcz: getting hacky.  this indicates that we clicked on a PDF excerpt quotation.  In this case, we don't want to follow the link (we follow only the actual hyperlink for the quotation which is handled above).
-                    href = link && link.attrs.href;
-                    location = link && link.attrs.location;
-                }
-            }
-            if (href) {
-                if (href.indexOf(Utils.prepend("/doc/")) === 0) {
-                    let linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                    if (linkClicked) {
-                        DocServer.GetRefField(linkClicked).then(async linkDoc => {
-                            (linkDoc instanceof Doc) &&
-                                DocumentManager.Instance.FollowLink(linkDoc, this.props.Document, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), false);
-                        });
-                    }
-                } else {
-                    let webDoc = Docs.Create.WebDocument(href, { x: NumCast(this.layoutDoc.x, 0) + NumCast(this.layoutDoc.width, 0), y: NumCast(this.layoutDoc.y) });
-                    this.props.addDocument && this.props.addDocument(webDoc);
-                }
-                e.stopPropagation();
-                e.preventDefault();
-            }
-        }
+        // if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) {
+        //     let href = (e.target as any).href;
+        //     let location: string;
+        //     if ((e.target as any).attributes.location) {
+        //         location = (e.target as any).attributes.location.value;
+        //     }
+        //     let pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+        //     let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos);
+        //     if (node) {
+        //         let link = node.marks.find(m => m.type === this._editorView!.state.schema.marks.link);
+        //         if (link && !(link.attrs.docref && link.attrs.title)) {  // bcz: getting hacky.  this indicates that we clicked on a PDF excerpt quotation.  In this case, we don't want to follow the link (we follow only the actual hyperlink for the quotation which is handled above).
+        //             href = link && link.attrs.href;
+        //             location = link && link.attrs.location;
+        //         }
+        //     }
+        //     if (href) {
+        //         if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+        //             let linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+        //             if (linkClicked) {
+        //                 DocServer.GetRefField(linkClicked).then(async linkDoc => {
+        //                     (linkDoc instanceof Doc) &&
+        //                         DocumentManager.Instance.FollowLink(linkDoc, this.props.Document, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), false);
+        //                 });
+        //             }
+        //         } else {
+        //             let webDoc = Docs.Create.WebDocument(href, { x: NumCast(this.layoutDoc.x, 0) + NumCast(this.layoutDoc.width, 0), y: NumCast(this.layoutDoc.y) });
+        //             this.props.addDocument && this.props.addDocument(webDoc);
+        //         }
+        //         e.stopPropagation();
+        //         e.preventDefault();
+        //     }
+        // }
 
         this.hitBulletTargets(e.clientX, e.clientY, e.nativeEvent.offsetX, e.shiftKey);
         if (this._recording) setTimeout(() => { this.stopDictation(true); setTimeout(() => this.recordDictation(), 500); }, 500);
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 2a7467108..f35ca502f 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -9,6 +9,9 @@ import { Utils } from "../../../Utils";
 import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { DocUtils } from "../../documents/Documents";
+import { isBuffer } from "util";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DocumentType } from "../../documents/DocumentTypes";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -52,6 +55,7 @@ export class FormattedTextBoxComment {
     static mark: Mark;
     static opened: boolean;
     static textBox: FormattedTextBox | undefined;
+    static linkDoc: Doc | undefined;
     constructor(view: any) {
         if (!FormattedTextBoxComment.tooltip) {
             const root = document.getElementById("root");
@@ -66,10 +70,15 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
+                if (FormattedTextBoxComment.linkDoc && !keep && FormattedTextBoxComment.textBox) {
+                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.textBox.props.Document,
+                        (doc: Doc, maxLocation: string) => FormattedTextBoxComment.textBox!.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
                 FormattedTextBoxComment.textBox && FormattedTextBoxComment.start !== undefined && FormattedTextBoxComment.textBox.setAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
                     FormattedTextBoxComment.opened, keep);
+                e.stopPropagation();
             };
             root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
@@ -96,6 +105,7 @@ export class FormattedTextBoxComment {
             lastState.selection.eq(state.selection)) {
             return;
         }
+        FormattedTextBoxComment.linkDoc = undefined;
 
         const textBox = FormattedTextBoxComment.textBox;
         if (!textBox || !textBox.props) {
@@ -132,10 +142,11 @@ export class FormattedTextBoxComment {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
                         if (linkDoc instanceof Doc) {
+                            FormattedTextBoxComment.linkDoc = linkDoc;
                             let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
-                            let ext = (target && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
+                            let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
                             let text = ext && StrCast(ext.text);
-                            ext && (FormattedTextBoxComment.tooltipText.textContent = "=> " + (text || StrCast(ext.title)));
+                            ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
                         }
                     });
                 }
-- 
cgit v1.2.3-70-g09d2


From 76411ba009c4265751431d255ab3cc9a3cfab69f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 12 Nov 2019 21:28:39 -0500
Subject: switched link preview to be a document view

---
 src/client/util/RichTextSchema.tsx                 |  1 -
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +
 src/client/views/nodes/DocumentView.tsx            |  6 ++-
 src/client/views/nodes/FormattedTextBoxComment.tsx | 62 +++++++++++++++++-----
 4 files changed, 54 insertions(+), 17 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 76b8aeaa1..1004cb3d4 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -18,7 +18,6 @@ import { Transform } from "./Transform";
 import React = require("react");
 import { BoolCast, NumCast } from "../../new_fields/Types";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 
 const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index f41c4fc91..751355403 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -37,6 +37,7 @@ interface ContentFittingDocumentViewProps {
     whenActiveChanged: (isActive: boolean) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
+    dontRegisterView?: boolean;
     setPreviewScript: (script: string) => void;
     previewScript?: string;
 }
@@ -108,6 +109,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         focus={this.props.focus || emptyFunction}
                         backgroundColor={returnEmptyString}
                         bringToFront={emptyFunction}
+                        dontRegisterView={this.props.dontRegisterView}
                         zoomToScale={emptyFunction}
                         getScale={returnOne}
                     />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 93052ea73..4fbb0516f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -74,6 +74,7 @@ export interface DocumentViewProps {
     getScale: () => number;
     animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
     ChromeHeight?: () => number;
+    dontRegisterView?: boolean;
     layoutKey?: string;
 }
 
@@ -98,7 +99,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     componentDidMount() {
         this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
-        DocumentManager.Instance.DocumentViews.push(this);
+
+        !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
     }
 
     @action
@@ -111,7 +113,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     componentWillUnmount() {
         this._dropDisposer && this._dropDisposer();
         Doc.UnBrushDoc(this.props.Document);
-        DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+        !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
     startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index f35ca502f..98cd17972 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -2,16 +2,19 @@ import { Plugin, EditorState } from "prosemirror-state";
 import './FormattedTextBoxComment.scss';
 import { ResolvedPos, Mark } from "prosemirror-model";
 import { EditorView } from "prosemirror-view";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym } from "../../../new_fields/Doc";
 import { schema } from "../../util/RichTextSchema";
 import { DocServer } from "../../DocServer";
-import { Utils } from "../../../Utils";
-import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
+import { Utils, returnTrue, returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
+import { StrCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { FormattedTextBox } from "./FormattedTextBox";
-import { DocUtils } from "../../documents/Documents";
-import { isBuffer } from "util";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentView } from "./DocumentView";
+import React = require("react");
+import * as ReactDOM from 'react-dom';
+import { Transform } from "../../util/Transform";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -67,22 +70,28 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
             FormattedTextBoxComment.tooltip.style.maxWidth = "350px";
+            FormattedTextBoxComment.tooltip.style.maxHeight = "250px";
+            FormattedTextBoxComment.tooltip.style.width = "100%";
+            FormattedTextBoxComment.tooltip.style.height = "100%";
+            FormattedTextBoxComment.tooltip.style.overflow = "hidden";
+            FormattedTextBoxComment.tooltip.style.display = "none";
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
-                if (FormattedTextBoxComment.linkDoc && !keep && FormattedTextBoxComment.textBox) {
-                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.textBox.props.Document,
-                        (doc: Doc, maxLocation: string) => FormattedTextBoxComment.textBox!.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                const textBox = FormattedTextBoxComment.textBox;
+                if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
+                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
+                        (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
                 }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
-                FormattedTextBoxComment.textBox && FormattedTextBoxComment.start !== undefined && FormattedTextBoxComment.textBox.setAnnotation(
+                textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
                     FormattedTextBoxComment.opened, keep);
                 e.stopPropagation();
             };
             root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
-        this.update(view, undefined);
+        //this.update(view, undefined);
     }
 
     public static Hide() {
@@ -144,9 +153,34 @@ export class FormattedTextBoxComment {
                         if (linkDoc instanceof Doc) {
                             FormattedTextBoxComment.linkDoc = linkDoc;
                             let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
-                            let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
-                            let text = ext && StrCast(ext.text);
-                            ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
+                            try {
+                                ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+                            } catch (e) {
+
+                            }
+                            target && ReactDOM.render(<ContentFittingDocumentView
+                                fitToBox={true}
+                                Document={target}
+                                fieldKey={"data"}
+                                moveDocument={returnFalse}
+                                getTransform={Transform.Identity}
+                                active={returnFalse}
+                                setPreviewScript={returnEmptyString}
+                                addDocument={returnFalse}
+                                removeDocument={returnFalse}
+                                ruleProvider={undefined}
+                                addDocTab={returnFalse}
+                                pinToPres={returnFalse}
+                                dontRegisterView={true}
+                                renderDepth={1}
+                                PanelWidth={() => 350}
+                                PanelHeight={() => 250}
+                                focus={emptyFunction}
+                                whenActiveChanged={returnFalse}
+                            />, FormattedTextBoxComment.tooltipText);
+                            // let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
+                            // let text = ext && StrCast(ext.text);
+                            // ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
                         }
                     });
                 }
@@ -168,5 +202,5 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
-    destroy() { FormattedTextBoxComment.tooltip.style.display = "none"; }
+    destroy() { }//FormattedTextBoxComment.tooltip.style.display = "none"; }
 }
-- 
cgit v1.2.3-70-g09d2


From a93ee99c9dd3b5ecc5160f98f9dffeb119b4dcce Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 13 Nov 2019 13:46:23 -0500
Subject: fixed recentlyClosed on startup and added clearAll.  fixed textbox
 comment layout a bit.

---
 src/client/util/SearchUtil.ts                      |  7 ++-
 .../views/collections/CollectionStackingView.tsx   |  1 -
 .../views/collections/CollectionTreeView.tsx       | 13 ++--
 .../views/nodes/ContentFittingDocumentView.tsx     |  1 -
 src/client/views/nodes/FormattedTextBox.tsx        |  5 +-
 .../views/nodes/FormattedTextBoxComment.scss       |  1 -
 src/client/views/nodes/FormattedTextBoxComment.tsx | 71 +++++++++++-----------
 .../views/presentationview/PresElementBox.tsx      |  1 -
 .../authentication/models/current_user_utils.ts    |  2 +
 9 files changed, 56 insertions(+), 46 deletions(-)

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

diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 6706dcb89..2cf13680a 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -28,6 +28,7 @@ export namespace SearchUtil {
         start?: number;
         rows?: number;
         fq?: string;
+        allowAliases?: boolean;
     }
     export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
     export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
@@ -73,7 +74,7 @@ export namespace SearchUtil {
         const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
         for (let i = 0; i < ids.length; i++) {
             let testDoc = docs[i];
-            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
+            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
                 theDocs.push(testDoc);
                 theLines.push([]);
             }
@@ -88,9 +89,9 @@ export namespace SearchUtil {
         const proto = Doc.GetProto(doc);
         const protoId = proto[Id];
         if (returnDocs) {
-            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).docs;
+            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).docs;
         } else {
-            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).ids;
+            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).ids;
         }
         // return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 15033e51a..be3bfca0a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -168,7 +168,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
-            fieldKey={this.props.fieldKey}
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8726726bb..0e3f0d1a9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -23,7 +23,6 @@ import { EditableView } from "../EditableView";
 import { MainView } from '../MainView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { Templates } from '../Templates';
-import { CollectionViewType } from './CollectionView';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
@@ -177,8 +176,10 @@ class TreeView extends React.Component<TreeViewProps> {
     />)
 
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking && this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
+        if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view
+            if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) {
+                ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
+            } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
                 ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
                 ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
@@ -317,7 +318,6 @@ class TreeView extends React.Component<TreeViewProps> {
                 <ContentFittingDocumentView
                     Document={layoutDoc}
                     DataDocument={this.templateDataDoc}
-                    fieldKey={this.fieldKey}
                     renderDepth={this.props.renderDepth}
                     showOverlays={this.noOverlays}
                     ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
@@ -538,6 +538,11 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+        } else if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.recentlyClosed) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => CurrentUserUtils.UserDocument.recentlyClosed = new List<Doc>(), icon: "plus" });
+            e.stopPropagation();
+            e.preventDefault();
+            ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
         } else {
             let layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: this.props.Document.preventTreeViewOpen ? "Persist Treeview State" : "Abandon Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 751355403..c8255b6fe 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -20,7 +20,6 @@ interface ContentFittingDocumentViewProps {
     childDocs?: Doc[];
     renderDepth: number;
     fitToBox?: boolean;
-    fieldKey: string;
     PanelWidth: () => number;
     PanelHeight: () => number;
     ruleProvider: Doc | undefined;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 65a51b357..8b1e65663 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -843,7 +843,10 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
-        if (!(e.nativeEvent as any).formattedHandled) { FormattedTextBoxComment.textBox = this; }
+        if (!(e.nativeEvent as any).formattedHandled) {
+            FormattedTextBoxComment.textBox = this;
+            FormattedTextBoxComment.update(this._editorView!);
+        }
         (e.nativeEvent as any).formattedHandled = true;
 
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.scss b/src/client/views/nodes/FormattedTextBoxComment.scss
index 792cee182..2dd63ec21 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/FormattedTextBoxComment.scss
@@ -5,7 +5,6 @@
     background: white;
     border: 1px solid silver;
     border-radius: 2px;
-    padding: 2px 10px;
     margin-bottom: 7px;
     -webkit-transform: translateX(-50%);
     transform: translateX(-50%);
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 98cd17972..29b4b6383 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -1,20 +1,18 @@
-import { Plugin, EditorState } from "prosemirror-state";
-import './FormattedTextBoxComment.scss';
-import { ResolvedPos, Mark } from "prosemirror-model";
+import { Mark, ResolvedPos } from "prosemirror-model";
+import { EditorState, Plugin } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
-import { Doc, WidthSym } from "../../../new_fields/Doc";
-import { schema } from "../../util/RichTextSchema";
+import * as ReactDOM from 'react-dom';
+import { Doc } from "../../../new_fields/Doc";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { emptyFunction, returnEmptyString, returnFalse, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
-import { Utils, returnTrue, returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
-import { StrCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { FormattedTextBox } from "./FormattedTextBox";
 import { DocumentManager } from "../../util/DocumentManager";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentView } from "./DocumentView";
-import React = require("react");
-import * as ReactDOM from 'react-dom';
+import { schema } from "../../util/RichTextSchema";
 import { Transform } from "../../util/Transform";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import { FormattedTextBox } from "./FormattedTextBox";
+import './FormattedTextBoxComment.scss';
+import React = require("react");
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -107,7 +105,7 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
     }
 
-    update(view: EditorView, lastState?: EditorState) {
+    static update(view: EditorView, lastState?: EditorState) {
         let state = view.state;
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
@@ -122,6 +120,8 @@ export class FormattedTextBoxComment {
         }
         let set = "none";
         let nbef = 0;
+        FormattedTextBoxComment.tooltip.style.width = "";
+        FormattedTextBoxComment.tooltip.style.height = "";
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
@@ -152,32 +152,35 @@ export class FormattedTextBoxComment {
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
                         if (linkDoc instanceof Doc) {
                             FormattedTextBoxComment.linkDoc = linkDoc;
-                            let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
+                            const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
                             try {
                                 ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
                             } catch (e) {
 
                             }
-                            target && ReactDOM.render(<ContentFittingDocumentView
-                                fitToBox={true}
-                                Document={target}
-                                fieldKey={"data"}
-                                moveDocument={returnFalse}
-                                getTransform={Transform.Identity}
-                                active={returnFalse}
-                                setPreviewScript={returnEmptyString}
-                                addDocument={returnFalse}
-                                removeDocument={returnFalse}
-                                ruleProvider={undefined}
-                                addDocTab={returnFalse}
-                                pinToPres={returnFalse}
-                                dontRegisterView={true}
-                                renderDepth={1}
-                                PanelWidth={() => 350}
-                                PanelHeight={() => 250}
-                                focus={emptyFunction}
-                                whenActiveChanged={returnFalse}
-                            />, FormattedTextBoxComment.tooltipText);
+                            if (target) {
+                                ReactDOM.render(<ContentFittingDocumentView
+                                    fitToBox={true}
+                                    Document={target}
+                                    moveDocument={returnFalse}
+                                    getTransform={Transform.Identity}
+                                    active={returnFalse}
+                                    setPreviewScript={returnEmptyString}
+                                    addDocument={returnFalse}
+                                    removeDocument={returnFalse}
+                                    ruleProvider={undefined}
+                                    addDocTab={returnFalse}
+                                    pinToPres={returnFalse}
+                                    dontRegisterView={true}
+                                    renderDepth={1}
+                                    PanelWidth={() => Math.min(350, NumCast(target.width, 350))}
+                                    PanelHeight={() => Math.min(250, NumCast(target.height, 250))}
+                                    focus={emptyFunction}
+                                    whenActiveChanged={returnFalse}
+                                />, FormattedTextBoxComment.tooltipText);
+                                FormattedTextBoxComment.tooltip.style.width = NumCast(target.width) ? `${NumCast(target.width)}` : "100%";
+                                FormattedTextBoxComment.tooltip.style.height = NumCast(target.height) ? `${NumCast(target.height)}` : "100%";
+                            }
                             // let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
                             // let text = ext && StrCast(ext.text);
                             // ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 17b2094ec..f50a3a0ef 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -172,7 +172,6 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
                 <ContentFittingDocumentView
                     fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     Document={this.targetDoc}
-                    fieldKey={this.props.fieldKey}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 833e44bf6..466e2d707 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -185,6 +185,8 @@ export class CurrentUserUtils {
         (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc);
         (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc);
 
+        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
+        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
         // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
         PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
         PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => {
-- 
cgit v1.2.3-70-g09d2


From c6b602e2ab00d3a38e56164ecc928d5db8c12c72 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 14 Nov 2019 11:35:33 -0500
Subject: fixed warning/errors.  cleaned up link following box a bit.

---
 src/client/util/RichTextSchema.tsx                 |  4 ++--
 src/client/util/TooltipTextMenu.tsx                | 20 ++++++++-----------
 .../views/collections/CollectionSchemaView.tsx     |  1 -
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx | 23 ++++++++++++++++------
 src/client/views/search/FilterBox.tsx              |  4 ++--
 8 files changed, 32 insertions(+), 25 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 0d1ae3841..0fc526ca7 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -575,8 +575,8 @@ export class ImageResizeView {
         this._handle.onpointerdown = function (e: any) {
             e.preventDefault();
             e.stopPropagation();
-            let wid = Number(getComputedStyle(self._img).width!.replace(/px/, ""));
-            let hgt = Number(getComputedStyle(self._img).height!.replace(/px/, ""));
+            let wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
+            let hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
             const startX = e.pageX;
             const startWidth = parseFloat(node.attrs.width);
             const onpointermove = (e: any) => {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 38471a955..5e11ae653 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1000,12 +1000,10 @@ export class TooltipTextMenu {
         let activeMarks: MarkType[];
         if (!empty) {
             activeMarks = markGroup.filter(mark => {
-                if (dispatch) {
-                    let has = false;
-                    for (let i = 0; !has && i < ranges.length; i++) {
-                        let { $from, $to } = ranges[i];
-                        return state.doc.rangeHasMark($from.pos, $to.pos, mark);
-                    }
+                let has = false;
+                for (let i = 0; !has && i < ranges.length; i++) {
+                    let { $from, $to } = ranges[i];
+                    return state.doc.rangeHasMark($from.pos, $to.pos, mark);
                 }
                 return false;
             });
@@ -1024,13 +1022,11 @@ export class TooltipTextMenu {
                 }
                 this._activeMarks = ref_node.marks;
                 activeMarks = markGroup.filter(mark_type => {
-                    if (dispatch) {
-                        if (mark_type === state.schema.marks.pFontSize) {
-                            return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
-                        }
-                        let mark = state.schema.mark(mark_type);
-                        return ref_node.marks.includes(mark);
+                    if (mark_type === state.schema.marks.pFontSize) {
+                        return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
                     }
+                    let mark = state.schema.mark(mark_type);
+                    return ref_node.marks.includes(mark);
                     return false;
                 });
             }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 203c68463..ebd47fd19 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -141,7 +141,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             <ContentFittingDocumentView
                 Document={layoutDoc}
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
-                fieldKey={this.props.fieldKey}
                 childDocs={this.childDocs}
                 renderDepth={this.props.renderDepth}
                 ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98c610c68..7132f181e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -639,7 +639,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     {searchHighlight}
                 </div>
             }
-        </>
+        </>;
     }
     render() {
         if (!this.props.Document) return (null);
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index ae9273639..83ecc4657 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -25,7 +25,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         this._backgroundReaction = reaction(() => this.props.Document.backgroundColor,
             () => {
                 if (this._ref && this._ref.current) {
-                    let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor!);
+                    let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
                     let colsum = (col.r + col.g + col.b);
                     if (colsum / col.a > 600 || col.a < 0.25) runInAction(() => this._foregroundColor = "black");
                     else if (colsum / col.a <= 600 || col.a >= .25) runInAction(() => this._foregroundColor = "white");
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 015a21fd2..6d60e8f77 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -288,6 +288,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
         if (context === node) return { from: offset, to: offset + node.nodeSize };
 
         if (node.isBlock) {
+            // tslint:disable-next-line: prefer-for-of
             for (let i = 0; i < (context.content as any).content.length; i++) {
                 let result = this.getNodeEndpoints((context.content as any).content[i], node);
                 if (result) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 29b4b6383..32efb16cf 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -13,6 +13,7 @@ import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import './FormattedTextBoxComment.scss';
 import React = require("react");
+import { Docs } from "../../documents/Documents";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -51,6 +52,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
 export class FormattedTextBoxComment {
     static tooltip: HTMLElement;
     static tooltipText: HTMLElement;
+    static tooltipInput: HTMLInputElement;
     static start: number;
     static end: number;
     static mark: Mark;
@@ -60,10 +62,15 @@ export class FormattedTextBoxComment {
     constructor(view: any) {
         if (!FormattedTextBoxComment.tooltip) {
             const root = document.getElementById("root");
-            let input = document.createElement("input");
-            input.type = "checkbox";
+            FormattedTextBoxComment.tooltipInput = document.createElement("input");
+            FormattedTextBoxComment.tooltipInput.type = "checkbox";
             FormattedTextBoxComment.tooltip = document.createElement("div");
             FormattedTextBoxComment.tooltipText = document.createElement("div");
+            FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
+            FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
+            FormattedTextBoxComment.tooltipText.style.width = "100%";
+            FormattedTextBoxComment.tooltipText.style.height = "100%";
+            FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
@@ -73,13 +80,15 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.style.height = "100%";
             FormattedTextBoxComment.tooltip.style.overflow = "hidden";
             FormattedTextBoxComment.tooltip.style.display = "none";
-            FormattedTextBoxComment.tooltip.appendChild(input);
+            FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
                         (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { width: 200, height: 400 }), undefined, "onRight");
                 }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
                 textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
@@ -120,8 +129,10 @@ export class FormattedTextBoxComment {
         }
         let set = "none";
         let nbef = 0;
+        FormattedTextBoxComment.tooltipInput.style.display = "none";
         FormattedTextBoxComment.tooltip.style.width = "";
         FormattedTextBoxComment.tooltip.style.height = "";
+        (FormattedTextBoxComment.tooltipText as any).href = "";
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
@@ -136,6 +147,7 @@ export class FormattedTextBoxComment {
             if (mark && child && ((nbef && naft) || !noselection)) {
                 FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
                 set = "";
+                FormattedTextBoxComment.tooltipInput.style.display = "";
             }
         }
         // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
@@ -147,6 +159,7 @@ export class FormattedTextBoxComment {
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
+                (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
@@ -155,9 +168,7 @@ export class FormattedTextBoxComment {
                             const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
                             try {
                                 ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
-                            } catch (e) {
-
-                            }
+                            } catch (e) { }
                             if (target) {
                                 ReactDOM.render(<ContentFittingDocumentView
                                     fitToBox={true}
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 6e7e4ee1b..62f3aba4c 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -82,7 +82,7 @@ export class FilterBox extends React.Component {
                     var panel = this.nextElementSibling as HTMLElement;
                     if (panel.style.maxHeight) {
                         panel.style.overflow = "hidden";
-                        panel.style.maxHeight = null;
+                        panel.style.maxHeight = "";
                         panel.style.opacity = "0";
                     } else {
                         setTimeout(() => {
@@ -114,7 +114,7 @@ export class FilterBox extends React.Component {
                     acc[i].classList.toggle("active");
                     var panel = acc[i].nextElementSibling as HTMLElement;
                     panel.style.overflow = "hidden";
-                    panel.style.maxHeight = null;
+                    panel.style.maxHeight = "";
                 }
             }
         });
-- 
cgit v1.2.3-70-g09d2


From 71275370c00112f87b0ec1044a26fe644b5bfba5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 14 Nov 2019 11:59:17 -0500
Subject: tweaks to text box comment

---
 package.json                                       |  1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx | 11 +++++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

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

diff --git a/package.json b/package.json
index 40bf40c17..591c8cff3 100644
--- a/package.json
+++ b/package.json
@@ -221,6 +221,7 @@
         "typescript-collections": "^1.3.2",
         "url-loader": "^1.1.2",
         "uuid": "^3.3.2",
+        "wikijs": "^6.0.1",
         "words-to-numbers": "^1.5.1",
         "xoauth2": "^1.2.0",
         "youtube": "^0.1.0"
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 32efb16cf..04958525c 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -14,6 +14,7 @@ import { FormattedTextBox } from "./FormattedTextBox";
 import './FormattedTextBoxComment.scss';
 import React = require("react");
 import { Docs } from "../../documents/Documents";
+import wiki from "wikijs";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -66,8 +67,6 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltipInput.type = "checkbox";
             FormattedTextBoxComment.tooltip = document.createElement("div");
             FormattedTextBoxComment.tooltipText = document.createElement("div");
-            FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
-            FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
             FormattedTextBoxComment.tooltipText.style.width = "100%";
             FormattedTextBoxComment.tooltipText.style.height = "100%";
             FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
@@ -133,6 +132,8 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip.style.width = "";
         FormattedTextBoxComment.tooltip.style.height = "";
         (FormattedTextBoxComment.tooltipText as any).href = "";
+        FormattedTextBoxComment.tooltipText.style.whiteSpace = "";
+        FormattedTextBoxComment.tooltipText.style.overflow = "";
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
@@ -159,6 +160,12 @@ export class FormattedTextBoxComment {
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
+                if (mark.attrs.href.startsWith("https://en.wikipedia.org/wiki/")) {
+                    wiki().page(mark.attrs.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
+                } else {
+                    FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
+                    FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
+                }
                 (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-- 
cgit v1.2.3-70-g09d2


From c9fbdb9cfd5fcf35f7a599706cb41d4e7e586e19 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 14 Nov 2019 16:23:57 -0500
Subject: added optional sidebar for text notes

---
 src/client/views/DocumentDecorations.tsx           | 11 +-----
 src/client/views/nodes/FormattedTextBox.scss       | 12 +++++++
 src/client/views/nodes/FormattedTextBox.tsx        | 39 +++++++++++++++++++---
 src/client/views/nodes/FormattedTextBoxComment.tsx |  5 ++-
 4 files changed, 49 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 55c211d1d..49921170f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -157,22 +157,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         this.onBackgroundUp(e);
     }
 
-    @observable _forceUpdate = 0;
-    _lastBox = { x: 0, y: 0, r: 0, b: 0 };
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
-        let x = this._forceUpdate;
-        this._lastBox = SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
+        return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
             if (documentView.props.renderDepth === 0 ||
                 Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
                 return bounds;
             }
             let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
-            if (transform.TranslateX === 0 && transform.TranslateY === 0) {
-                setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
-                return this._lastBox;
-            }
-
             var [sptX, sptY] = transform.transformPoint(0, 0);
             let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
             if (documentView.props.Document.type === DocumentType.LINK) {
@@ -187,7 +179,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
             };
         }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
-        return this._lastBox;
     }
 
     onBackgroundDown = (e: React.PointerEvent): void => {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index a4acd3b82..98b57bd84 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -36,6 +36,18 @@
     }
 }
 
+.collectionfreeformview-container {
+    position: relative;
+}
+.formattedTextBox-outer {
+    position: relative;
+    overflow: auto;
+    display: inline-block;
+    padding: 10px 10px;
+    width: 100%;
+    height: 100%;
+}
+
 .formattedTextBox-inner-rounded {
     height: 70%;
     width: 85%;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 6d60e8f77..b7df3fb0e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -18,7 +18,7 @@ import { RichTextField } from "../../../new_fields/RichTextField";
 import { RichTextUtils } from '../../../new_fields/RichTextUtils';
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { Cast, DateCast, NumCast, StrCast } from "../../../new_fields/Types";
-import { numberRange, Utils, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from '../../../Utils';
+import { numberRange, Utils, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnOne } from '../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -33,7 +33,7 @@ import { SelectionManager } from "../../util/SelectionManager";
 import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DocExtendableComponent } from "../DocComponent";
+import { DocAnnotatableComponent } from "../DocComponent";
 import { DocumentButtonBar } from '../DocumentButtonBar';
 import { DocumentDecorations } from '../DocumentDecorations';
 import { InkingControl } from "../InkingControl";
@@ -46,6 +46,7 @@ import { ContextMenu } from '../ContextMenu';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { AudioBox } from './AudioBox';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -70,7 +71,7 @@ const RichTextDocument = makeInterface(richTextSchema, documentSchema);
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
 @observer
-export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
     public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
     public static Instance: FormattedTextBox;
@@ -359,6 +360,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
 
     specificContextMenu = (e: React.MouseEvent): void => {
         let funcs: ContextMenuProps[] = [];
+        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"; }, icon: "expand-arrows-alt" });
         funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
             funcs.push({
@@ -1010,6 +1012,9 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
         }
     }
 
+    @computed get sidebarWidthPercent() { return StrCast(this.props.Document.sidebarWidth, "0%"); }
+    @computed get sidebarWidth() { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); }
+    @computed get annotationsKey() { return "annotations"; }
     render() {
         trace();
         let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
@@ -1042,8 +1047,32 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
                 onPointerEnter={action(() => this._entered = true)}
                 onPointerLeave={action(() => this._entered = false)}
             >
-                <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
-
+                <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }}>
+                    <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
+                </div>
+                {this.sidebarWidthPercent === "0%" ? (null) : <div style={{ borderLeft: "solid 1px black", width: `${this.sidebarWidthPercent}`, height: "100%", display: "inline-block" }}>
+                    <CollectionFreeFormView {...this.props}
+                        PanelHeight={() => this.props.PanelHeight()}
+                        PanelWidth={() => this.sidebarWidth}
+                        annotationsKey={this.annotationsKey}
+                        isAnnotationOverlay={true}
+                        focus={this.props.focus}
+                        isSelected={this.props.isSelected}
+                        select={emptyFunction}
+                        active={this.active}
+                        ContentScaling={returnOne}
+                        whenActiveChanged={this.whenActiveChanged}
+                        removeDocument={this.removeDocument}
+                        moveDocument={this.moveDocument}
+                        addDocument={this.addDocument}
+                        CollectionView={undefined}
+                        ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
+                        ruleProvider={undefined}
+                        renderDepth={this.props.renderDepth + 1}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        chromeCollapsed={true}>
+                    </CollectionFreeFormView>
+                </div>}
                 <div className="formattedTextBox-dictation"
                     onClick={e => {
                         this._recording ? this.stopDictation(true) : this.recordDictation();
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 04958525c..c076fd34a 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -87,7 +87,7 @@ export class FormattedTextBoxComment {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
                         (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
-                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { width: 200, height: 400 }), undefined, "onRight");
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, width: 200, height: 400 }), undefined, "onRight");
                 }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
                 textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
@@ -97,7 +97,6 @@ export class FormattedTextBoxComment {
             };
             root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
-        //this.update(view, undefined);
     }
 
     public static Hide() {
@@ -223,5 +222,5 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
-    destroy() { }//FormattedTextBoxComment.tooltip.style.display = "none"; }
+    destroy() { }
 }
-- 
cgit v1.2.3-70-g09d2

