From 301784883375860a7c693db97fe8f279e5380caf Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Sat, 19 Oct 2019 16:51:45 -0400
Subject: image stuff

---
 src/client/views/RecommendationsBox.tsx | 185 ++++++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)
 create mode 100644 src/client/views/RecommendationsBox.tsx

(limited to 'src/client/views/RecommendationsBox.tsx')

diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
new file mode 100644
index 000000000..3938a8690
--- /dev/null
+++ b/src/client/views/RecommendationsBox.tsx
@@ -0,0 +1,185 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { observable, action } from "mobx";
+import Measure from "react-measure";
+import "./RecommendationsBox.scss";
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { DocumentIcon } from "./nodes/DocumentIcon";
+import { StrCast, NumCast } from "../../new_fields/Types";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../Utils";
+import { Transform } from "../util/Transform";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { DocumentView } from "./nodes/DocumentView";
+import { DocumentType } from '../documents/DocumentTypes';
+import { ClientRecommender } from "../ClientRecommender";
+import { DocServer } from "../DocServer";
+import { Id } from "../../new_fields/FieldSymbols";
+import { FieldView, FieldViewProps } from "./nodes/FieldView";
+import { DocumentManager } from "../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { library } from "@fortawesome/fontawesome-svg-core";
+import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons";
+import { DocUtils } from "../documents/Documents";
+
+export interface RecProps {
+    documents: { preview: Doc, similarity: number }[];
+    node: Doc;
+}
+
+library.add(faBullseye, faLink);
+
+@observer
+export class RecommendationsBox extends React.Component<FieldViewProps> {
+
+    public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); }
+
+    static Instance: RecommendationsBox;
+    // @observable private _display: boolean = false;
+    @observable private _pageX: number = 0;
+    @observable private _pageY: number = 0;
+    @observable private _width: number = 0;
+    @observable private _height: number = 0;
+    // @observable private _documents: { preview: Doc, score: number }[] = [];
+    private previewDocs: Doc[] = [];
+
+    constructor(props: FieldViewProps) {
+        super(props);
+        RecommendationsBox.Instance = this;
+    }
+
+    private DocumentIcon(doc: Doc) {
+        let layoutresult = StrCast(doc.type);
+        let renderDoc = doc;
+        //let box: number[] = [];
+        if (layoutresult.indexOf(DocumentType.COL) !== -1) {
+            renderDoc = Doc.MakeDelegate(renderDoc);
+            let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
+                var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+                let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+                return {
+                    x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+                    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 });
+        }
+        let returnXDimension = () => 150;
+        let returnYDimension = () => 150;
+        let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+        //let scale = () => 1;
+        let newRenderDoc = Doc.MakeAlias(renderDoc); ///   newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
+        newRenderDoc.height = NumCast(this.props.Document.documentIconHeight);
+        newRenderDoc.autoHeight = false;
+        const docview = <div>
+            <DocumentView
+                fitToBox={StrCast(doc.type).indexOf(DocumentType.COL) !== -1}
+                Document={newRenderDoc}
+                addDocument={returnFalse}
+                removeDocument={returnFalse}
+                ruleProvider={undefined}
+                ScreenToLocalTransform={Transform.Identity}
+                addDocTab={returnFalse}
+                pinToPres={returnFalse}
+                renderDepth={1}
+                PanelWidth={returnXDimension}
+                PanelHeight={returnYDimension}
+                focus={emptyFunction}
+                backgroundColor={returnEmptyString}
+                parentActive={returnFalse}
+                whenActiveChanged={returnFalse}
+                bringToFront={emptyFunction}
+                zoomToScale={emptyFunction}
+                getScale={returnOne}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+                ContentScaling={scale}
+            />
+        </div>;
+        return docview;
+
+    }
+
+    // @action
+    // closeMenu = () => {
+    //     this._display = false;
+    //     this.previewDocs.forEach(doc => DocServer.DeleteDocument(doc[Id]));
+    //     this.previewDocs = [];
+    // }
+
+    // @action
+    // resetDocuments = () => {
+    //     this._documents = [];
+    // }
+
+    // @action
+    // displayRecommendations(x: number, y: number) {
+    //     this._pageX = x;
+    //     this._pageY = y;
+    //     this._display = true;
+    // }
+
+    static readonly buffer = 20;
+
+    // get pageX() {
+    //     const x = this._pageX;
+    //     if (x < 0) {
+    //         return 0;
+    //     }
+    //     const width = this._width;
+    //     if (x + width > window.innerWidth - RecommendationsBox.buffer) {
+    //         return window.innerWidth - RecommendationsBox.buffer - width;
+    //     }
+    //     return x;
+    // }
+
+    // get pageY() {
+    //     const y = this._pageY;
+    //     if (y < 0) {
+    //         return 0;
+    //     }
+    //     const height = this._height;
+    //     if (y + height > window.innerHeight - RecommendationsBox.buffer) {
+    //         return window.innerHeight - RecommendationsBox.buffer - height;
+    //     }
+    //     return y;
+    // }
+
+    render() {
+        // if (!this._display) {
+        //     return null;
+        // }
+        // let style = { left: this.pageX, top: this.pageY };
+        //const transform = "translate(" + (NumCast(this.props.node.x) + 350) + "px, " + NumCast(this.props.node.y) + "px"
+        let title = StrCast((this.props.Document.sourceDoc as Doc).title);
+        if (title.length > 15) {
+            title = title.substring(0, 15) + "...";
+        }
+        return (
+            // <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
+            // {({ measureRef }) => (
+            <div className="rec-scroll">
+                <p>Recommendations for "{title}"</p>
+                {DocListCast(this.props.Document.data).map(doc => {
+                    return (
+                        <div className="content">
+                            <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+                                {this.DocumentIcon(doc)}
+                            </span>
+                            <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+                            <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+                            </div>
+                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+                            </div>
+                        </div>
+                    );
+                })}
+
+            </div>
+            // );
+            // }
+
+            // </Measure>
+        );
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 1676ddcf9f4da6f9926e2e48eea4302ae9f2efe5 Mon Sep 17 00:00:00 2001
From: ab <abdullah_ahmed@brown.edu>
Date: Fri, 25 Oct 2019 16:21:45 -0400
Subject: images show up, react errors

---
 src/client/ClientRecommender.tsx                   | 78 ++++++++++++----------
 src/client/cognitive_services/CognitiveServices.ts |  5 +-
 src/client/views/RecommendationsBox.tsx            |  2 +-
 3 files changed, 47 insertions(+), 38 deletions(-)

(limited to 'src/client/views/RecommendationsBox.tsx')

diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 97efedd89..ae2413d1d 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -1,4 +1,4 @@
-import { Doc } from "../new_fields/Doc";
+import { Doc, FieldResult } from "../new_fields/Doc";
 import { StrCast, Cast } from "../new_fields/Types";
 import { List } from "../new_fields/List";
 import { CognitiveServices } from "./cognitive_services/CognitiveServices";
@@ -147,47 +147,55 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
      */
 
     public async extractText(dataDoc: Doc, extDoc: Doc, internal: boolean = true, isMainDoc: boolean = false, image: boolean = false) {
-        let fielddata = Cast(dataDoc.data, RichTextField);
-        if (image && extDoc.generatedTags) {
-            console.log(Cast(extDoc.generatedTags, listSpec("string")));
+        let data: string = "";
+        let taglist: FieldResult<List<string>> = undefined;
+        if (image && extDoc.generatedTags) { // TODO: Automatically generate tags. Need to ask Sam about this.
+            taglist = Cast(extDoc.generatedTags, listSpec("string"));
+            taglist!.forEach(tag => {
+                data += tag + ", ";
+            });
+        }
+        else {
+            let fielddata = Cast(dataDoc.data, RichTextField);
+            fielddata ? data = fielddata[ToPlainText]() : data = "";
         }
-        let data: string;
-        fielddata ? data = fielddata[ToPlainText]() : data = "";
-        let converter = async (results: any, data: string) => {
+
+        let converter = async (results: any, data: string, isImage: boolean = false) => {
             let keyterms = new List<string>(); // raw keywords
             // let keyterms_counted = new List<string>(); // keywords, where each keyword is repeated. input to w2v
             let kp_string: string = ""; // keywords*frequency concatenated into a string. input into TF
             let highKP: string[] = [""]; // most frequent keyphrase
             let high = 0;
-            results.documents.forEach((doc: any) => {
-                let keyPhrases = doc.keyPhrases;
-                keyPhrases.map((kp: string) => {
-                    keyterms.push(kp);
-                    const frequency = this.countFrequencies(kp, data); // frequency of keyphrase in paragraph
-                    kp_string += kp + ", "; // ensures that if frequency is 0 for some reason kp is still added
-                    for (let i = 0; i < frequency - 1; i++) {
-                        kp_string += kp + ", "; // weights repeated keywords higher
-                    }
-                    // replaces highKP with new one
-                    if (frequency > high) {
-                        high = frequency;
-                        highKP = [kp];
-                    }
-                    // appends to current highKP phrase
-                    else if (frequency === high) {
-                        highKP.push(kp);
-                    }
-                    // let words = kp.split(" "); // separates phrase into words
-                    // words = this.removeStopWords(words); // removes stop words if they appear in phrases
-                    // words.forEach((word) => {
-                    //     for (let i = 0; i < frequency; i++) {
-                    //         keyterms_counted.push(word);
-                    //     }
-                    // });
+
+            if (isImage) {
+                kp_string = data;
+                if (taglist) {
+                    keyterms = taglist;
+                    highKP = [taglist[0]];
+                }
+            }
+            else { // text processing
+                results.documents.forEach((doc: any) => {
+                    let keyPhrases = doc.keyPhrases;
+                    keyPhrases.map((kp: string) => {
+                        keyterms.push(kp);
+                        const frequency = this.countFrequencies(kp, data); // frequency of keyphrase in paragraph
+                        kp_string += kp + ", "; // ensures that if frequency is 0 for some reason kp is still added
+                        for (let i = 0; i < frequency - 1; i++) {
+                            kp_string += kp + ", "; // weights repeated keywords higher
+                        }
+                        // replaces highKP with new one
+                        if (frequency > high) {
+                            high = frequency;
+                            highKP = [kp];
+                        }
+                        // appends to current highKP phrase
+                        else if (frequency === high) {
+                            highKP.push(kp);
+                        }
+                    });
                 });
-            });
-            // const kts_counted = new List<string>();
-            // keyterms_counted.forEach(kt => kts_counted.push(kt.toLowerCase()));
+            }
             if (kp_string.length > 2) kp_string = kp_string.substring(0, kp_string.length - 2);
             console.log("kp string: ", kp_string);
             let values = "";
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index b0e9138a4..d496b442e 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -9,6 +9,7 @@ import { UndoManager } from "../util/UndoManager";
 import requestPromise = require("request-promise");
 import { List } from "../../new_fields/List";
 import { ClientRecommender } from "../ClientRecommender";
+import { ImageBox } from "../views/nodes/ImageBox";
 
 type APIManager<D> = { converter: BodyConverter<D>, requester: RequestExecutor };
 type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise<string>;
@@ -288,12 +289,12 @@ export namespace CognitiveServices {
                 );
             }
 
-            export const analyzer = async (dataDoc: Doc, target: Doc, keys: string[], data: string, converter: TextConverter, isMainDoc: boolean = false, internal: boolean = true) => {
+            export const analyzer = async (dataDoc: Doc, target: Doc, keys: string[], data: string, converter: TextConverter, isMainDoc: boolean = false, isInternal: boolean = true) => {
                 let results = await ExecuteQuery(Service.Text, Manager, data);
                 console.log(results);
                 let { keyterms, external_recommendations, kp_string } = await converter(results, data);
                 target[keys[0]] = keyterms;
-                if (internal) {
+                if (isInternal) {
                     //await vectorize([data], dataDoc, isMainDoc);
                     await vectorize(kp_string, dataDoc, isMainDoc);
                 } else {
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index 3938a8690..c50550bef 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -143,7 +143,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
     //     return y;
     // }
 
-    render() {
+    render() { //TODO: Invariant violation: max depth exceeded error. Occurs when images are rendered. 
         // if (!this._display) {
         //     return null;
         // }
-- 
cgit v1.2.3-70-g09d2


From 96dbd7d4fd1759ad1a5135ca94d46970ca31168f Mon Sep 17 00:00:00 2001
From: monikahedman <monika_hedman@brown.edu>
Date: Wed, 30 Oct 2019 18:26:11 -0400
Subject: ui issues, max depth

---
 package.json                            |  5 ++-
 src/client/views/RecommendationsBox.tsx | 70 +++++++++++++++++++++------------
 2 files changed, 47 insertions(+), 28 deletions(-)

(limited to 'src/client/views/RecommendationsBox.tsx')

diff --git a/package.json b/package.json
index b3cf9102b..fde685eba 100644
--- a/package.json
+++ b/package.json
@@ -57,8 +57,9 @@
         "@hig/theme-context": "^2.1.3",
         "@hig/theme-data": "^2.3.3",
         "@tensorflow-models/universal-sentence-encoder": "^1.2.0",
-        "@tensorflow/tfjs": "^1.2.9",
-        "@tensorflow/tfjs-node": "^1.2.11",
+        "@tensorflow/tfjs": "^1.3.1",
+        "@tensorflow/tfjs-core": "^1.3.1",
+        "@tensorflow/tfjs-node": "^1.3.1",
         "@trendmicro/react-dropdown": "^1.3.0",
         "@types/adm-zip": "^0.4.32",
         "@types/animejs": "^2.0.2",
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index c50550bef..fd128bfca 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -1,6 +1,6 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action } from "mobx";
+import { observable, action, computed } from "mobx";
 import Measure from "react-measure";
 import "./RecommendationsBox.scss";
 import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
@@ -33,18 +33,17 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
 
     public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); }
 
-    static Instance: RecommendationsBox;
     // @observable private _display: boolean = false;
     @observable private _pageX: number = 0;
     @observable private _pageY: number = 0;
     @observable private _width: number = 0;
     @observable private _height: number = 0;
+    private _docViews: JSX.Element[] = [];
     // @observable private _documents: { preview: Doc, score: number }[] = [];
     private previewDocs: Doc[] = [];
 
     constructor(props: FieldViewProps) {
         super(props);
-        RecommendationsBox.Instance = this;
     }
 
     private DocumentIcon(doc: Doc) {
@@ -143,6 +142,45 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
     //     return y;
     // }
 
+    get createDocViews() {
+        return DocListCast(this.props.Document.data).map(doc => {
+            return (
+                <div className="content">
+                    <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+                        {this.DocumentIcon(doc)}
+                    </span>
+                    <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+                    <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+                        <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+                    </div>
+                    <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+                        <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+                    </div>
+                </div>
+            );
+        });
+    }
+
+    // componentDidMount() {
+    //     this._docViews = DocListCast(this.props.Document.data).map(doc => {
+    //         return (
+    //             <div className="content">
+    //                 <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+    //                     {this.DocumentIcon(doc)}
+    //                 </span>
+    //                 <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+    //                 <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+    //                     <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+    //                 </div>
+    //                 <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+    //                     <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+    //                 </div>
+    //             </div>
+    //         );
+    //     })
+
+    // }
+
     render() { //TODO: Invariant violation: max depth exceeded error. Occurs when images are rendered. 
         // if (!this._display) {
         //     return null;
@@ -154,32 +192,12 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
             title = title.substring(0, 15) + "...";
         }
         return (
-            // <Measure offset onResize={action((r: any) => { this._width = r.offset.width; this._height = r.offset.height; })}>
-            // {({ measureRef }) => (
             <div className="rec-scroll">
                 <p>Recommendations for "{title}"</p>
-                {DocListCast(this.props.Document.data).map(doc => {
-                    return (
-                        <div className="content">
-                            <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
-                                {this.DocumentIcon(doc)}
-                            </span>
-                            <span className="score">{NumCast(doc.score).toFixed(4)}</span>
-                            <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
-                            </div>
-                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
-                            </div>
-                        </div>
-                    );
-                })}
-
+                {this.createDocViews}
             </div>
-            // );
-            // }
-
-            // </Measure>
         );
     }
+    // 
+    // 
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 3d5e2bf61e04fa700eccac10d19ea6dde436f6bd Mon Sep 17 00:00:00 2001
From: monikahedman <monika_hedman@brown.edu>
Date: Thu, 31 Oct 2019 11:57:08 -0400
Subject: fixed depth problem

---
 src/client/ClientRecommender.tsx        | 63 ++++++++++++++++++++++++++------
 src/client/views/RecommendationsBox.tsx | 64 ++++++++++++++++-----------------
 src/client/views/nodes/ImageBox.tsx     |  1 +
 3 files changed, 83 insertions(+), 45 deletions(-)

(limited to 'src/client/views/RecommendationsBox.tsx')

diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 90dd240b6..364ec0fe0 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -1,7 +1,7 @@
 import { Doc, FieldResult } from "../new_fields/Doc";
 import { StrCast, Cast } from "../new_fields/Types";
 import { List } from "../new_fields/List";
-import { CognitiveServices } from "./cognitive_services/CognitiveServices";
+import { CognitiveServices, Confidence, Tag, Service } from "./cognitive_services/CognitiveServices";
 import React = require("react");
 import { observer } from "mobx-react";
 import { observable, action, computed, reaction } from "mobx";
@@ -14,6 +14,8 @@ import { RichTextField } from "../new_fields/RichTextField";
 import { ToPlainText } from "../new_fields/FieldSymbols";
 import { listSpec } from "../new_fields/Schema";
 import { Identified } from "./Network";
+import { ComputedField } from "../new_fields/ScriptField";
+import { ImageField } from "../new_fields/URLField";
 
 export interface RecommenderProps {
     title: string;
@@ -31,9 +33,13 @@ export interface RecommenderDocument {
     score: number;
 }
 
+const fieldkey = "data";
+
 @observer
 export class ClientRecommender extends React.Component<RecommenderProps> {
 
+
+
     static Instance: ClientRecommender;
     private mainDoc?: RecommenderDocument;
     private docVectors: Set<RecommenderDocument> = new Set();
@@ -45,10 +51,6 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
         super(props);
         if (!ClientRecommender.Instance) ClientRecommender.Instance = this;
         ClientRecommender.Instance.docVectors = new Set();
-        const parameters: any = {};
-        Identified.PostToServer("/IBMAnalysis", parameters).then(response => {
-            console.log("ANALYSIS RESULTS! ", response);
-        });
         //ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]];
     }
 
@@ -97,6 +99,10 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
      */
 
     public computeSimilarities(distance_metric: string) {
+        const parameters: any = {};
+        Identified.PostToServer("/IBMAnalysis", parameters).then(response => {
+            console.log("ANALYSIS RESULTS! ", response);
+        });
         ClientRecommender.Instance.docVectors.forEach((doc: RecommenderDocument) => {
             if (ClientRecommender.Instance.mainDoc) {
                 const distance = ClientRecommender.Instance.distance(ClientRecommender.Instance.mainDoc.vectorDoc, doc.vectorDoc, distance_metric);
@@ -147,6 +153,39 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
         }
     }
 
+    /***
+     * Generates tags for an image using Cognitive Services
+     */
+
+    generateMetadata = async (dataDoc: Doc, extDoc: Doc, threshold: Confidence = Confidence.Excellent) => {
+        let converter = (results: any) => {
+            let tagDoc = new Doc;
+            let tagsList = new List();
+            results.tags.map((tag: Tag) => {
+                tagsList.push(tag.name);
+                let sanitized = tag.name.replace(" ", "_");
+                tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
+            });
+            extDoc.generatedTags = tagsList;
+            tagDoc.title = "Generated Tags Doc";
+            tagDoc.confidence = threshold;
+            return tagDoc;
+        };
+        const url = this.url(dataDoc);
+        if (url) {
+            return CognitiveServices.Image.Appliers.ProcessImage(extDoc, ["generatedTagsDoc"], url, Service.ComputerVision, converter);
+        }
+    }
+
+    /***
+     * Gets URL of image
+     */
+
+    private url(dataDoc: Doc) {
+        let data = Cast(Doc.GetProto(dataDoc)[fieldkey], ImageField);
+        return data ? data.url.href : undefined;
+    }
+
     /***
      * Uses Cognitive Services to extract keywords from a document
      */
@@ -154,17 +193,19 @@ export class ClientRecommender extends React.Component<RecommenderProps> {
     public async extractText(dataDoc: Doc, extDoc: Doc, internal: boolean = true, isMainDoc: boolean = false, image: boolean = false) {
         let data: string = "";
         let taglist: FieldResult<List<string>> = undefined;
-        if (image && extDoc.generatedTags) { // TODO: Automatically generate tags. Need to ask Sam about this.
-            taglist = Cast(extDoc.generatedTags, listSpec("string"));
-            taglist!.forEach(tag => {
-                data += tag + ", ";
-            });
+        if (image) {
+            if (!extDoc.generatedTags) await this.generateMetadata(dataDoc, extDoc);  // TODO: Automatically generate tags. Need to ask Sam about this.
+            if (extDoc.generatedTags) {
+                taglist = Cast(extDoc.generatedTags, listSpec("string"));
+                taglist!.forEach(tag => {
+                    data += tag + ", ";
+                });
+            }
         }
         else {
             let fielddata = Cast(dataDoc.data, RichTextField);
             fielddata ? data = fielddata[ToPlainText]() : data = "";
         }
-
         let converter = async (results: any, data: string, isImage: boolean = false) => {
             let keyterms = new List<string>(); // raw keywords
             // let keyterms_counted = new List<string>(); // keywords, where each keyword is repeated. input to w2v
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index fd128bfca..0e3cfd729 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -1,6 +1,6 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { observable, action, computed } from "mobx";
+import { observable, action, computed, runInAction } from "mobx";
 import Measure from "react-measure";
 import "./RecommendationsBox.scss";
 import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
@@ -38,7 +38,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
     @observable private _pageY: number = 0;
     @observable private _width: number = 0;
     @observable private _height: number = 0;
-    private _docViews: JSX.Element[] = [];
+    @observable.shallow private _docViews: JSX.Element[] = [];
     // @observable private _documents: { preview: Doc, score: number }[] = [];
     private previewDocs: Doc[] = [];
 
@@ -46,20 +46,13 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
+    @action
     private DocumentIcon(doc: Doc) {
         let layoutresult = StrCast(doc.type);
         let renderDoc = doc;
         //let box: number[] = [];
         if (layoutresult.indexOf(DocumentType.COL) !== -1) {
             renderDoc = Doc.MakeDelegate(renderDoc);
-            let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
-                var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-                let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
-                return {
-                    x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
-                    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 });
         }
         let returnXDimension = () => 150;
         let returnYDimension = () => 150;
@@ -142,27 +135,8 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
     //     return y;
     // }
 
-    get createDocViews() {
-        return DocListCast(this.props.Document.data).map(doc => {
-            return (
-                <div className="content">
-                    <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
-                        {this.DocumentIcon(doc)}
-                    </span>
-                    <span className="score">{NumCast(doc.score).toFixed(4)}</span>
-                    <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
-                        <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
-                    </div>
-                    <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
-                        <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
-                    </div>
-                </div>
-            );
-        });
-    }
-
-    // componentDidMount() {
-    //     this._docViews = DocListCast(this.props.Document.data).map(doc => {
+    // get createDocViews() {
+    //     return DocListCast(this.props.Document.data).map(doc => {
     //         return (
     //             <div className="content">
     //                 <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
@@ -177,10 +151,32 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
     //                 </div>
     //             </div>
     //         );
-    //     })
-
+    //     });
     // }
 
+    componentDidMount() { //TODO: invoking a computedFn from outside an reactive context won't be memoized, unless keepAlive is set
+        runInAction(() => {
+            if (this._docViews.length === 0) {
+                this._docViews = DocListCast(this.props.Document.data).map(doc => {
+                    return (
+                        <div className="content">
+                            <span style={{ height: NumCast(this.props.Document.documentIconHeight) }} className="image-background">
+                                {this.DocumentIcon(doc)}
+                            </span>
+                            <span className="score">{NumCast(doc.score).toFixed(4)}</span>
+                            <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
+                            </div>
+                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
+                            </div>
+                        </div>
+                    );
+                });
+            }
+        });
+    }
+
     render() { //TODO: Invariant violation: max depth exceeded error. Occurs when images are rendered. 
         // if (!this._display) {
         //     return null;
@@ -194,7 +190,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
         return (
             <div className="rec-scroll">
                 <p>Recommendations for "{title}"</p>
-                {this.createDocViews}
+                {this._docViews}
             </div>
         );
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 7816c0fe8..1bf2724c3 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -248,6 +248,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     onDotDown(index: number) {
         this.Document.curPage = index;
     }
+
     @computed private get url() {
         let data = Cast(Doc.GetProto(this.props.Document)[this.props.fieldKey], ImageField);
         return data ? data.url.href : undefined;
-- 
cgit v1.2.3-70-g09d2


From ef58bdb2f6e9bffe377239d77b3360ed8b3132a1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 3 Mar 2020 18:14:37 -0500
Subject: a bunch of small fixes to link naming and fixes to audio links.

---
 src/client/documents/Documents.ts                             |  8 ++++----
 src/client/util/RichTextMenu.tsx                              |  2 +-
 src/client/util/RichTextRules.ts                              |  2 +-
 src/client/util/RichTextSchema.tsx                            |  3 ++-
 src/client/views/DocumentButtonBar.tsx                        |  4 +---
 src/client/views/RecommendationsBox.tsx                       |  2 +-
 src/client/views/collections/CollectionTreeView.tsx           |  2 +-
 .../collectionFreeForm/CollectionFreeFormLinkView.tsx         |  4 ++--
 .../views/collections/collectionFreeForm/MarqueeView.tsx      | 11 ++++++-----
 src/client/views/nodes/AudioBox.scss                          |  3 ++-
 src/client/views/nodes/AudioBox.tsx                           |  1 +
 src/client/views/nodes/DocumentView.tsx                       | 10 +++++-----
 src/client/views/nodes/FormattedTextBox.tsx                   |  4 ++--
 src/client/views/nodes/VideoBox.tsx                           |  2 +-
 src/client/views/pdf/PDFViewer.tsx                            |  4 ++--
 15 files changed, 32 insertions(+), 30 deletions(-)

(limited to 'src/client/views/RecommendationsBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 67e037286..6b25b3897 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -459,7 +459,7 @@ export namespace Docs {
             const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
             const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
 
-            viewDoc.type !== DocumentType.LINK && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title));
+            viewDoc.type !== DocumentType.LINK && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "audio timeline"));
 
             return Doc.assign(viewDoc, delegateProps, true);
         }
@@ -914,13 +914,13 @@ export namespace DocUtils {
         });
     }
 
-    export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", linkRelationship: string = "", id?: string) {
+    export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, linkRelationship: string = "", id?: string) {
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === CurrentUserUtils.UserDocument) return undefined;
 
-        const linkDoc = Docs.Create.LinkDocument(source, target, { title, linkRelationship }, id);
-        Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" " + (this.linkRelationship||"to") +" "  + this.anchor2.title');
+        const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id);
+        Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" (" + (this.linkRelationship||"to") +") "  + this.anchor2.title');
 
         Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)");
         Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)");
diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx
index 460f1fa37..3f0ec7aa5 100644
--- a/src/client/util/RichTextMenu.tsx
+++ b/src/client/util/RichTextMenu.tsx
@@ -146,7 +146,7 @@ export default class RichTextMenu extends AntimodeMenu {
 
     public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => {
         if (this.view) {
-            const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId });
+            const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId });
             this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
                 addMark(this.view.state.selection.from, this.view.state.selection.to, link));
             return this.view.state.selection.$from.nodeAfter?.text || "";
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 7ffc2dd9c..e5b20a79b 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -91,7 +91,7 @@ export class RichTextRules {
                             DocServer.GetRefField(docid).then(docx => {
                                 const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true, }, docid);
                                 DocUtils.Publish(target, docid, returnFalse, returnFalse);
-                                DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal link", "");
+                                DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
                             });
                             const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
                             return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 034f5d55a..2c3714310 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -301,6 +301,7 @@ export const marks: { [index: string]: MarkSpec } = {
         attrs: {
             href: {},
             targetId: { default: "" },
+            linkId: { default: "" },
             showPreview: { default: true },
             location: { default: null },
             title: { default: null },
@@ -315,7 +316,7 @@ export const marks: { [index: string]: MarkSpec } = {
         toDOM(node: any) {
             return node.attrs.docref && node.attrs.title ?
                 ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] :
-                ["a", { ...node.attrs, id: node.attrs.targetId, title: `${node.attrs.title}` }, 0];
+                ["a", { ...node.attrs, id: node.attrs.linkId + node.attrs.targetId, title: `${node.attrs.title}` }, 0];
         }
     },
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index a3d313224..52544d3c9 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -122,13 +122,11 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
                     if (this.view0 && linkDoc) {
                         const proto = Doc.GetProto(linkDoc);
                         proto.anchor1Context = this.view0.props.ContainingCollectionDoc;
+                        Doc.GetProto(linkDoc).linkRelationship = "hyperlink";
 
                         const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
                         const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
                         const text = RichTextMenu.Instance.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", anchor2Id);
-                        if (linkDoc.anchor2 instanceof Doc && !proto.title) {
-                            proto.title = Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" " + (this.linkRelationship||"to") +" "  + this.anchor2.title');
-                        }
                     }
                     linkDrag?.end();
                 },
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index 0e3cfd729..262226bac 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -167,7 +167,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
                             <div style={{ marginRight: 50 }} onClick={() => DocumentManager.Instance.jumpToDocument(doc, false)}>
                                 <FontAwesomeIcon className="documentdecorations-icon" icon={"bullseye"} size="sm" />
                             </div>
-                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "User Selected Link", "Generated from Recommender", undefined)}>
+                            <div style={{ marginRight: 50 }} onClick={() => DocUtils.MakeLink({ doc: this.props.Document.sourceDoc as Doc }, { doc: doc }, "Recommender", undefined)}>
                                 <FontAwesomeIcon className="documentdecorations-icon" icon={"link"} size="sm" />
                             </div>
                         </div>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7eeeb6ff1..28f620157 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -231,7 +231,7 @@ class TreeView extends React.Component<TreeViewProps> {
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceDocument;
             const destDoc = this.props.document;
-            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree drop link");
+            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link");
             e.stopPropagation();
         }
         if (de.complete.docDragData) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 1038347d4..a33146388 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -46,8 +46,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 // really hacky stuff to make the DocuLinkBox display where we want it to:
                 //   if there's an element in the DOM with the id of the opposite anchor, then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
                 //   otherwise, we just use the computed nearest point on the document boundary to the target Document
-                const targetAhyperlink = window.document.getElementById((this.props.LinkDocs[0][afield] as Doc)[Id]);
-                const targetBhyperlink = window.document.getElementById((this.props.LinkDocs[0][bfield] as Doc)[Id]);
+                const targetAhyperlink = window.document.getElementById(this.props.LinkDocs[0][Id] + (this.props.LinkDocs[0][afield] as Doc)[Id]);
+                const targetBhyperlink = window.document.getElementById(this.props.LinkDocs[0][Id] + (this.props.LinkDocs[0][bfield] as Doc)[Id]);
                 if (!targetBhyperlink) {
                     this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
                     this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index af701347f..4bf3329eb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -109,7 +109,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
             this.props.addLiveTextDocument(
                 Docs.Create.TextDocument("", { _width: NumCast((FormattedTextBox.DefaultLayout as Doc)?._width) || 200, _height: 100, layout: FormattedTextBox.DefaultLayout, x: x, y: y, _autoHeight: true, title: "-typed text-" }));
-        } 
+        }
         e.stopPropagation();
     }
     //heuristically converts pasted text into a table.
@@ -435,13 +435,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.page = -1;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "-summary-" });
+        const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
         const portal = Doc.MakeAlias(summary);
-        Doc.GetProto(summary)["data-annotations"] = new List<Doc>(selected);
-        Doc.GetProto(summary).layout_portal = CollectionView.LayoutString("data-annotations");
+        Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
+        Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
         summary._backgroundColor = "#e2ad32";
         portal.layoutKey = "layout_portal";
-        DocUtils.MakeLink({ doc: summary, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, "portal link", "portal link");
+        portal.title = "document collection";
+        DocUtils.MakeLink({ doc: summary, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, "summarizing");
 
         this.props.addLiveTextDocument(summary);
         MarqueeOptionsMenu.Instance.fadeOut(true);
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 0c363f0c1..4516418a7 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -72,6 +72,7 @@
                     margin-left:-2.55px;
                     background:gray;
                     border-radius: 100%;
+                    opacity:0.9;
                     background-color: transparent;
                     box-shadow: black 2px 2px 1px;
                     .docuLinkBox-cont {
@@ -98,7 +99,7 @@
                     }
                 }
                 .audiobox-linker:hover, .audiobox-linker-mini:hover {
-                    transform:scale(1.5);
+                    opacity:1;
                 }
                 .audiobox-marker-container, .audiobox-marker-minicontainer {
                     position:absolute;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 862578e40..c4c6365e3 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -249,6 +249,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                                     <div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={l[Id]} style={{ left: `${linkTime / NumCast(this.dataDoc.duration, 1) * 100}%` }}>
                                         <div className={this.props.PanelHeight() < 32 ? "audioBox-linker-mini" : "audioBox-linker"} key={"linker" + i}>
                                             <DocumentView {...this.props} Document={l} layoutKey={Doc.LinkEndpoint(l, la2)}
+                                                ContainingCollectionDoc={this.props.Document}
                                                 parentActive={returnTrue} bringToFront={emptyFunction} zoomToScale={emptyFunction} getScale={returnOne}
                                                 backgroundColor={returnTransparent} />
                                         </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 64d85589f..782a9ce08 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -569,8 +569,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             de.complete.annoDragData.linkedToDoc = true;
 
-            DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc },
-                `Link from ${StrCast(de.complete.annoDragData.annotationDocument.title)}`);
+            DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "link");
         }
         if (de.complete.docDragData) {
             if (de.complete.docDragData.applyAsTemplate) {
@@ -601,13 +600,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
             de.complete.linkDragData.linkSourceDocument !== this.props.Document &&
                 (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument },
-                    { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `link from ${de.complete.linkDragData.linkSourceDocument.title} to ${this.props.Document.title}`)); // TODODO this is where in text links get passed
+                    { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `link`)); // TODODO this is where in text links get passed
         }
     }
 
     @undoBatch
     @action
     public static unfreezeNativeDimensions(layoutDoc: Doc) {
+        m
         layoutDoc._nativeWidth = undefined;
         layoutDoc._nativeHeight = undefined;
     }
@@ -627,7 +627,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
             const portal = Docs.Create.FreeformDocument([], { _width: (this.layoutDoc._width || 0) + 10, _height: this.layoutDoc._height || 0, title: StrCast(this.props.Document.title) + ".portal" });
-            DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, "portal link", "portal link");
+            DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, "portal to");
         }
         this.Document.isButton = true;
     }
@@ -1118,7 +1118,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
         let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear;
         highlighting = highlighting && this.props.focus !== emptyFunction;  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
-        return <div id={this.props.Document[Id]} className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} onKeyDown={this.onKeyDown}
+        return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
             onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
             style={{
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7b434ebc1..8f4cefbf4 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -150,7 +150,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                     this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500 }, value);
                     DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument);
                     if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
-                    else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id);
+                    else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "link to named target", id);
                 });
             });
         });
@@ -723,7 +723,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                             targetAnnotations?.push(pdfRegion);
                         });
 
-                        const link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link");
+                        const link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "PDF pasted");
                         if (link) {
                             cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
                             const linkId = link[Id];
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 69c6f2617..7ab650dc9 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -134,7 +134,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
                     });
                     imageSummary.isButton = true;
                     this.props.addDocument && this.props.addDocument(imageSummary);
-                    DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "snapshot from " + this.Document.title, "video frame snapshot");
+                    DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "video snapshot");
                 }
             });
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0607dfda5..af06a2646 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -211,7 +211,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         await this.initialLoad();
 
         this._scrollTopReactionDisposer = reaction(() => Cast(this.props.Document._scrollTop, "number", null),
-            (stop) => (stop !== undefined) && this._mainCont.current && smoothScroll(500, this._mainCont.current, stop) , { fireImmediately: true });
+            (stop) => (stop !== undefined) && this._mainCont.current && smoothScroll(500, this._mainCont.current, stop), { fireImmediately: true });
         this._annotationReactionDisposer = reaction(
             () => DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]),
             annotations => annotations?.length && (this._annotations = annotations),
@@ -582,7 +582,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
             DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
                 dragComplete: e => {
                     if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
-                        const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument, ctx: e.annoDragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF");
+                        const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument, ctx: e.annoDragData.targetContext }, "Annotation");
                         if (link) link.maximizeLocation = "onRight";
                     }
                 }
-- 
cgit v1.2.3-70-g09d2

