From cad4ca15ec12808915b7aa901859e349144d8a50 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 2 Mar 2020 14:42:23 -0500
Subject: fixed pdfs to sort of support region clippings.

---
 src/client/views/pdf/PDFViewer.tsx | 50 +++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 22 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 198aeb856..b110aa172 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,7 +4,7 @@ import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { Dictionary } from "typescript-collections";
 import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id, Copy } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { makeInterface, createSchema } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
@@ -31,6 +31,7 @@ import { InkingControl } from "../InkingControl";
 import { InkTool } from "../../../new_fields/InkField";
 import { TraceMobx } from "../../../new_fields/util";
 import { PdfField } from "../../../new_fields/URLField";
+import { PDFBox } from "../nodes/PDFBox";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 
@@ -101,6 +102,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     private _reactionDisposer?: IReactionDisposer;
     private _selectionReactionDisposer?: IReactionDisposer;
     private _annotationReactionDisposer?: IReactionDisposer;
+    private _scrollTopReactionDisposer?: IReactionDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
     private _searchReactionDisposer?: IReactionDisposer;
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -126,7 +128,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         !this.props.Document.lockedTransform && (this.props.Document.lockedTransform = true);
         // change the address to be the file address of the PNG version of each page
         // file address of the pdf
-        const { url: { href } } = Cast(this.props.Document[this.props.fieldKey], PdfField)!;
+        const { url: { href } } = Cast(this.dataDoc[this.props.fieldKey], PdfField)!;
         const addr = Utils.prepend(`/thumbnail${this.props.url.substring("files/pdfs/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.png`);
         this._coverPath = href.startsWith(window.location.origin) ? JSON.parse(await rp.get(addr)) : { width: 100, height: 100, path: "" };
         runInAction(() => this._showWaiting = this._showCover = true);
@@ -162,10 +164,11 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
 
     componentWillUnmount = () => {
         this._reactionDisposer && this._reactionDisposer();
-        this._annotationReactionDisposer && this._annotationReactionDisposer();
-        this._filterReactionDisposer && this._filterReactionDisposer();
-        this._selectionReactionDisposer && this._selectionReactionDisposer();
-        this._searchReactionDisposer && this._searchReactionDisposer();
+        this._scrollTopReactionDisposer?.();
+        this._annotationReactionDisposer?.();
+        this._filterReactionDisposer?.();
+        this._selectionReactionDisposer?.();
+        this._searchReactionDisposer?.();
         document.removeEventListener("copy", this.copy);
     }
 
@@ -206,6 +209,13 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         this.props.setPdfViewer(this);
         await this.initialLoad();
 
+        this._scrollTopReactionDisposer = reaction(() => Cast(this.props.Document._scrollTop, "number", null),
+            (stop) => {
+                if (stop !== undefined) {
+                    const offset = this.visibleHeight() / 2 * 96 / 72;
+                    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),
@@ -267,7 +277,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         let minY = Number.MAX_VALUE;
         if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
             const anno = this._savedAnnotations.values()[0][0];
-            const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
+            const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, _LODdisable: true, title: "Annotation on " + this.Document.title });
             if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
             if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
             if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
@@ -558,28 +568,23 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         e.preventDefault();
         e.stopPropagation();
         const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
+        Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
+        const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
+        Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped");
+        Doc.GetProto(targetDoc).layout_snipped = snipLayout;
         const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
         if (annotationDoc) {
             DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
-                dragComplete: e => !e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc &&
-                    DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument, ctx: e.annoDragData.targetContext }, `Annotation from ${this.Document.title}`, "link from PDF")
+                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");
+                        if (link) link.maximizeLocation = "onRight";
+                    }
+                }
             });
         }
     }
 
-    createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
-        const view = Doc.MakeAlias(this.props.Document);
-        const data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
-        data.title = StrCast(data.title) + "_snippet";
-        view.proto = data;
-        view._nativeHeight = marquee.height;
-        view._height = (this.Document[WidthSym]() / (this.Document._nativeWidth || 1)) * marquee.height;
-        view._nativeWidth = this.Document._nativeWidth;
-        view.startY = marquee.top;
-        view._width = this.Document[WidthSym]();
-        DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
-    }
-
     scrollXf = () => {
         return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform();
     }
@@ -643,6 +648,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
                 select={emptyFunction}
                 active={this.annotationsActive}
                 ContentScaling={this.contentZoom}
+                bringToFront={emptyFunction}
                 whenActiveChanged={this.whenActiveChanged}
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
-- 
cgit v1.2.3-70-g09d2


From 2556a754404cc622629ca12bddcd163f53b69060 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 2 Mar 2020 17:34:37 -0500
Subject: changed clippings to use slide template.  fixed issues on startup

---
 src/client/util/DocumentManager.ts  |  2 +-
 src/client/views/nodes/PDFBox.tsx   |  4 ++--
 src/client/views/pdf/PDFViewer.scss |  8 ++++----
 src/client/views/pdf/PDFViewer.tsx  | 40 +++++++++++++++++++++----------------
 4 files changed, 30 insertions(+), 24 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4f721cb77..6bd2fd016 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -134,7 +134,7 @@ export class DocumentManager {
             finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
         };
         const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc, originatingDoc);
-        let annotatedDoc = await Cast(docView?.props.Document.annotationOn, Doc);
+        let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
         if (annotatedDoc) {
             const first = DocumentManager.Instance.getFirstDocumentView(annotatedDoc);
             if (first) annotatedDoc = first.props.Document;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7b545eee5..f47620c24 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -241,7 +241,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
                 ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
                 isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
                 isChildActive={this.isChildActive}
-                fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 ? true : false} />
+                fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 || this.props.Document._scrollTop ? true : false} />
             {this.settingsPanel()}
         </div>;
     }
@@ -250,7 +250,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
     render() {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
         if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true;
-        if (pdfUrl && (this._everActive || (this.dataDoc[this.props.fieldKey + "-nativeWidth"] && this.props.ScreenToLocalTransform().Scale < 2.5))) {
+        if (pdfUrl && (this._everActive || this.props.Document._scrollTop || (this.dataDoc[this.props.fieldKey + "-nativeWidth"] && this.props.ScreenToLocalTransform().Scale < 2.5))) {
             if (pdfUrl instanceof PdfField && this._pdf) {
                 return this.renderPdfView;
             }
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 4f81c6f70..5cd2c4fe4 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,6 +1,5 @@
  
-.pdfViewer, .pdfViewer-zoomed {
-    pointer-events: all;
+.pdfViewer, .pdfViewer-interactive {
     width: 100%;
     height: 100%;
     position: absolute;
@@ -91,7 +90,8 @@
         z-index: 10;
     }
 }
-.pdfViewer-zoomed {
-    overflow-x: scroll;
+
+.pdfViewer-interactive {
+    pointer-events: all;
 }
    
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index b110aa172..0607dfda5 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -32,6 +32,7 @@ import { InkTool } from "../../../new_fields/InkField";
 import { TraceMobx } from "../../../new_fields/util";
 import { PdfField } from "../../../new_fields/URLField";
 import { PDFBox } from "../nodes/PDFBox";
+import { FormattedTextBox } from "../nodes/FormattedTextBox";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 
@@ -210,12 +211,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         await this.initialLoad();
 
         this._scrollTopReactionDisposer = reaction(() => Cast(this.props.Document._scrollTop, "number", null),
-            (stop) => {
-                if (stop !== undefined) {
-                    const offset = this.visibleHeight() / 2 * 96 / 72;
-                    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),
@@ -237,6 +233,12 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         this.createPdfViewer();
     }
 
+    pagesinit = action(() => {
+        this._pdfViewer.currentScaleValue = this._zoomed = 1;
+        this.gotoPage(this.Document.curPage || 1);
+        document.removeEventListener("pagesinit", this.pagesinit);
+    })
+
     createPdfViewer() {
         if (!this._mainCont.current) { // bcz: I don't think this is ever triggered or needed
             if (this._retries < 5) {
@@ -247,10 +249,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         }
         document.removeEventListener("copy", this.copy);
         document.addEventListener("copy", this.copy);
-        document.addEventListener("pagesinit", action(() => {
-            this._pdfViewer.currentScaleValue = this._zoomed = 1;
-            this.gotoPage(this.Document.curPage || 1);
-        }));
+        document.addEventListener("pagesinit", this.pagesinit);
         document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false));
         const pdfLinkService = new PDFJSViewer.PDFLinkService();
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService });
@@ -330,7 +329,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
 
     @action
     gotoPage = (p: number) => {
-        this._pdfViewer && this._pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+        this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
     }
 
     @action
@@ -564,14 +563,20 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
      * start a drag event and create or put the necessary info into the drag event.
      */
     @action
-    startDrag = (e: PointerEvent, ele: HTMLElement): void => {
+    startDrag = async (e: PointerEvent, ele: HTMLElement) => {
         e.preventDefault();
         e.stopPropagation();
+
+        const clipDoc = Docs.Create.PdfDocument(Cast(this.dataDoc[this.props.fieldKey], PdfField, null)?.url.href || "http://www.msn.com", { title: "snippetView", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
         const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
-        Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
-        const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
-        Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped");
-        Doc.GetProto(targetDoc).layout_snipped = snipLayout;
+        Doc.GetProto(targetDoc).layout = FormattedTextBox.LayoutString("contents");
+        Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
+        Doc.GetProto(targetDoc).layout_slideView = (await Cast(Doc.UserDoc().slidesBtn, Doc))?.dragFactory;
+        targetDoc.layoutKey = "layout_slideView";
+        // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
+        // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
+        // const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
+        // Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped");
         const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
         if (annotationDoc) {
             DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
@@ -679,9 +684,10 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     contentZoom = () => this._zoomed;
     render() {
         TraceMobx();
-        return <div className={"pdfViewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont}
+        return <div className={"pdfViewer" + (this.active() ? "-interactive" : "")} ref={this._mainCont}
             onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
+                overflowX: this._zoomed !== 1 ? "scroll" : undefined,
                 width: !this.props.Document._fitWidth ? NumCast(this.props.Document._nativeWidth) : `${100 / this.contentScaling}%`,
                 height: !this.props.Document._fitWidth ? NumCast(this.props.Document._nativeHeight) : `${100 / this.contentScaling}%`,
                 transform: `scale(${this.props.ContentScaling()})`
-- 
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/pdf/PDFViewer.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

