From ff7df91f8502e3753e28d742ef8b5dd2ff33c801 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Jan 2021 13:09:55 -0500
Subject: adjustments to restore inkMask functionality

---
 src/client/views/nodes/DocumentView.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e0255405a..4c53ab07d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,6 +40,7 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { PresBox } from './PresBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
+import { InkingStroke } from "../InkingStroke";
 
 export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
 export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
@@ -965,9 +966,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             {!this.props.Document || !this.props.PanelWidth() ? (null) : (
                 <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
                     style={{
+                        position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: Math.abs(this.Xshift) > 0.001 ? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth(),
-                        height: Math.abs(this.YShift) > 0.001 ? this.props.Document._fitWidth ? `${this.panelHeight}px` : `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
+                        width: this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) > 0.001 ? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth(),
+                        height: this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.YShift) > 0.001 ? this.props.Document._fitWidth ? `${this.panelHeight}px` : `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
                     }}>
                     <DocumentViewInternal {...this.props} {...internalProps} ref={action((r: DocumentViewInternal | null) => this.docView = r)} />
                 </div>)}
-- 
cgit v1.2.3-70-g09d2


From a134dab9a458efcc8b4bd60489483e68f9332397 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Jan 2021 17:50:25 -0500
Subject: fixed layout of audioBox markers

---
 src/client/views/DocumentDecorations.tsx       |   3 +-
 src/client/views/nodes/AudioBox.scss           |   8 +-
 src/client/views/nodes/AudioBox.tsx            | 127 +++++++++++--------------
 src/client/views/nodes/DocumentLinksButton.tsx |   2 +
 src/client/views/nodes/DocumentView.tsx        |   7 +-
 src/client/views/nodes/ImageBox.tsx            |   2 +-
 src/client/views/nodes/VideoBox.tsx            |   2 +-
 src/client/views/nodes/WebBox.tsx              |   2 +-
 src/client/views/pdf/PDFViewer.tsx             |   2 +-
 9 files changed, 77 insertions(+), 78 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d7b0b5158..c2de78d09 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -62,7 +62,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
 
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
-        return SelectionManager.Views().map(dv => dv.getBounds()).reduce((bounds, rect) =>
+        const boudns = SelectionManager.Views().map(dv => dv.getBounds()).reduce((bounds, rect) =>
             !rect ? bounds :
                 {
                     x: Math.min(rect.left, bounds.x),
@@ -71,6 +71,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                     b: Math.max(rect.bottom, bounds.b)
                 },
             { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
+        return boudns;
     }
 
     titleBlur = action((commit: boolean) => {
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index e5bf9088f..aa3705205 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -300,10 +300,12 @@
 
                     .resizer {
                         position: absolute;
+                        top: 0;
                         right: 0;
+                        pointer-events: all;
                         cursor: ew-resize;
                         height: 100%;
-                        width: 2px;
+                        width: 10px;
                         z-index: 100;
                     }
 
@@ -317,9 +319,11 @@
                     .left-resizer {
                         position: absolute;
                         left: 0;
+                        top : 0;
+                        pointer-events: all;
                         cursor: ew-resize;
                         height: 100%;
-                        width: 2px;
+                        width: 10px;
                         z-index: 100;
                     }
                 }
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1c2812e86..98957d655 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -30,6 +30,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
 import { LinkAnchorBox } from "./LinkAnchorBox";
 import { LinkDocPreview } from "./LinkDocPreview";
+import { DocumentManager } from "../../util/DocumentManager";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -53,7 +54,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     // _linkPlayDisposer: IReactionDisposer | undefined;
     // _reactionDisposer: IReactionDisposer | undefined;
     // _scrubbingDisposer: IReactionDisposer | undefined;
-    private _disposers: { [name: string]: IReactionDisposer } = {};
+    _disposers: { [name: string]: IReactionDisposer } = {};
     _ele: HTMLAudioElement | null = null;
     _recorder: any;
     _recordStart = 0;
@@ -73,7 +74,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     _timeline: Opt<HTMLDivElement>;
     _duration = 0;
     _markerStart: number = 0;
-    private _currMarker: any;
+    _currMarker: any;
 
     @observable _visible: boolean = false;
     @observable _markerEnd: number = 0;
@@ -93,8 +94,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         AudioBox.Instance = this;
 
         // onClick play scripts
-        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.playFrom((this.audioStart), (this.audioEnd))`, { scriptContext: "any" })!;
-        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeScript(`scriptContext.playFrom((this.audioStart))`, { scriptContext: "any" })!;
+        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
     }
 
     getLinkData(l: Doc) {
@@ -116,17 +117,21 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._disposers.audioStart?.();
     }
 
+    getAnchor = () => {
+        return this._ele?.currentTime ? this.createMarker(this._ele?.currentTime) : this.rootDoc;
+    }
     @action
     componentDidMount() {
         if (!this.dataDoc.markerAmount) {
             this.dataDoc.markerAmount = 0;
         }
+        this.props.setContentView?.(this);
 
         this.audioState = this.path ? "paused" : undefined;
         this._disposers.linkPlay = reaction(() => this.layoutDoc.scrollToLinkID,
             scrollLinkId => {
                 if (scrollLinkId) {
-                    DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
+                    this.links.filter(l => l[Id] === scrollLinkId).map(l => {
                         const { linkTime } = this.getLinkData(l);
                         setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250);
                     });
@@ -139,7 +144,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             selected => {
                 const sel = selected.length ? selected[0].props.Document : undefined;
                 let link;
-                sel && DocListCast(this.dataDoc.links).forEach(l => {
+                sel && this.links.forEach(l => {
                     if (l.anchor1 === sel || l.anchor2 === sel && !sel.audioStart) {
                         link = this.playLink(sel);
                     }
@@ -153,38 +158,30 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._disposers.scrubbing = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
 
         this._disposers.audioStart = reaction(
-            () => this.Document._audioStart,
-            (audioStart) => {
-                if (audioStart !== undefined) {
-                    if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
-                        const delay = this._audioRef.current ? 0 : 250; // wait for mainCont and try again to play
-                        const startTime: number = NumCast(this.Document._audioStart);
-                        setTimeout(() => this._audioRef.current && this.playFrom(startTime), delay);
-                        setTimeout(() => { this.Document._currentTimecode = startTime; this.Document._audioStart = undefined; }, 10 + delay);
-                    }
-                }
-            },
+            () => !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this.props.renderDepth !== -1 ? Cast(this.Document._audioStart, "number", null) : undefined,
+            audioStart => audioStart !== undefined && setTimeout(() => {
+                this._audioRef.current && this.playFrom(audioStart);
+                setTimeout(() => {
+                    this.Document._currentTimecode = audioStart;
+                    this.Document._audioStart = undefined;
+                }, 10);
+            }, this._audioRef.current ? 0 : 250), // wait for mainCont and try again to play
             { fireImmediately: true }
         );
 
         this._disposers.audioStop = reaction(
-            () => this.Document._audioStop,
-            (audioStop) => {
-                if (audioStop !== undefined) {
-                    if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
-                        const delay = this._audioRef.current ? 0 : 250; // wait for mainCont and try again to play
-                        setTimeout(() => this._audioRef.current && this.pause(), delay);
-                        setTimeout(() => { this.Document._audioStop = undefined; }, 10 + delay);
-                    }
-                }
-            },
+            () => this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc ? Cast(this.Document._audioStop, "number", null) : undefined,
+            audioStop => audioStop !== undefined && setTimeout(() => {
+                this._audioRef.current && this.pause();
+                setTimeout(() => this.Document._audioStop = undefined, 10);
+            }, this._audioRef.current ? 0 : 250), // wait for mainCont and try again to play
             { fireImmediately: true }
         );
     }
 
     playLink = (doc: Doc) => {
         let link = false;
-        !Doc.AreProtosEqual(doc, this.props.Document) && DocListCast(this.props.Document.links).forEach(l => {
+        !Doc.AreProtosEqual(doc, this.props.Document) && this.links.forEach(l => {
             if (l.anchor1 === doc || l.anchor2 === doc) {
                 const { la1, la2, linkTime } = this.getLinkData(l);
                 let startTime = linkTime;
@@ -211,7 +208,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         const htmlEle = this._ele;
         if (this.audioState !== "recording" && htmlEle) {
             htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
-            DocListCast(this.dataDoc.links).map(l => {
+            this.links.map(l => {
                 const { la1, linkTime } = this.getLinkData(l);
                 if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) {
                     Doc.linkFollowHighlight(la1);
@@ -232,6 +229,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this.recordingStart && this.playFrom((absoluteTime - this.recordingStart) / 1000);
     }
 
+    // play back the audio from time
+    @action
+    playOnClick = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
+        DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false);
+        if (this.layoutDoc.playOnClick) this.playFrom(seekTimeInSeconds, endTime);
+        else this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds);
+    }
     // play back the audio from time
     @action
     playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
@@ -427,6 +431,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         } else {
             this.dataDoc[this.annotationKey] = new List<Doc>([marker]);
         }
+        return marker;
     }
 
     // starting the drag event for marker resizing
@@ -436,12 +441,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._timeline?.setPointerCapture(e.pointerId);
         const toTimeline = (screen_delta: number, width: number) => screen_delta / width * this.audioDuration;
         setupMoveUpEvents(this, e,
-            (e: PointerEvent) => {
+            (e) => {
                 const rect = (e.target as any).getBoundingClientRect();
                 this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x, rect.width));
                 return false;
             },
-            (e: PointerEvent) => {
+            (e) => {
                 const rect = (e.target as any).getBoundingClientRect();
                 this._ele!.currentTime = this.layoutDoc._currentTimecode = toTimeline(e.clientX - rect.x, rect.width);
                 this._timeline?.releasePointerCapture(e.pointerId);
@@ -452,7 +457,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     // updates the marker with the new time
     @action
     changeMarker = (m: any, time: any) => {
-        DocListCast(this.dataDoc[this.annotationKey]).filter(marker => this.isSame(marker, m)).forEach(marker =>
+        this.markerDocs.filter(marker => this.isSame(marker, m)).forEach(marker =>
             this._left ? marker.audioStart = time : marker.audioEnd = time);
     }
 
@@ -507,6 +512,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         }} />;
     }
 
+    @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
+    @computed get links() { return DocListCast(this.dataDoc.links); }
+
     // returns the audio waveform
     @computed get waveform() {
         const audioBuckets = Cast(this.dataDoc.audioBuckets, listSpec("number"), []);
@@ -539,12 +547,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
 
     rangeScript = () => AudioBox.RangeScript;
     labelScript = () => AudioBox.LabelScript;
-    static audioStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => {
+    audioStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => {
         if (property === StyleProp.BackgroundColor) return "transparent";
         if (property === StyleProp.PointerEvents) return "none";
+        return this.props.styleProvider?.(doc, props, property);
     }
     markerStyle = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string): any => {
-        return property.startsWith("backgroundColor") ? "dimGrey" : this.props.styleProvider?.(doc, props, property);
+        return property.startsWith("backgroundColor") ? "dimgrey" : this.props.styleProvider?.(doc, props, property);
     }
     render() {
         const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : "";
@@ -561,9 +570,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 LayoutTemplate={undefined}
                 ContainingCollectionDoc={this.props.Document}
                 removeDocument={this.removeDocument}
-                ScreenToLocalTransform={x && y ? () => this.props.ScreenToLocalTransform().translate(-x, -y) : this.props.ScreenToLocalTransform}
+                ScreenToLocalTransform={x && y ? () => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3) : this.props.ScreenToLocalTransform}
                 parentActive={returnTrue}
-                onClick={this.layoutDoc.playOnClick ? script : undefined}
+                onClick={script}
                 ignoreAutoHeight={false}
                 bringToFront={emptyFunction}
                 scriptContext={this} />;
@@ -601,9 +610,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                             onClick={e => { e.stopPropagation(); e.preventDefault(); }}
                             onPointerDown={e => {
                                 if (e.button === 0 && !e.ctrlKey) {
-                                    const rect = (e.target as any).getBoundingClientRect();
-
-                                    if (e.target !== this._audioRef.current) {
+                                    const rect = this._timeline?.getBoundingClientRect();// (e.target as any).getBoundingClientRect();
+                                    if (rect && e.target !== this._audioRef.current) {
                                         const wasPaused = this.audioState === "paused";
                                         this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
                                         wasPaused && this.pause();
@@ -615,57 +623,38 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                             <div className="waveform">
                                 {this.waveform}
                             </div>
-                            {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) => {
+                            {this.markerDocs.map((m, i) => {
                                 const isOverlap = this.isOverlap(m);
                                 return !m.isLabel ?
                                     this.layoutDoc.hideMarkers ? (null) :
                                         <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container1`} key={i}
                                             title={`${formatTime(Math.round(NumCast(m.audioStart)))}` + " - " + `${formatTime(Math.round(NumCast(m.audioEnd)))}`}
                                             style={{
-                                                left: `${NumCast(m.audioStart) / this.audioDuration * 100}%`,
-                                                top: `${isOverlap * 1 / (this.dataDoc.markerAmount + 1) * 100}%`,
+                                                left: `${NumCast(m.audioStart) / this.audioDuration * timelineContentWidth}px`,
+                                                top: `${isOverlap * 1 / (this.dataDoc.markerAmount + 1) * timelineContentHeight}px`,
                                                 width: `${(NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration * 100}%`,
                                                 height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%`
                                             }}
                                             onClick={e => { this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} >
-                                            <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)}></div>
+                                            <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)} />
                                             {markerDoc(m, this.rangeScript,
                                                 playheadWidth + NumCast(m.audioStart) / this.audioDuration * timelineContentWidth,
                                                 .1 * this.props.PanelHeight() + isOverlap / (this.dataDoc.markerAmount + 1) * timelineContentHeight,
                                                 timelineContentWidth * (NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration,
                                                 timelineContentHeight / (this.dataDoc.markerAmount + 1))}
-                                            <div className="resizer" onPointerDown={e => this.onPointerDown(e, m, false)}></div>
+                                            <div className="resizer" onPointerDown={e => this.onPointerDown(e, m, false)} />
                                         </div>
                                     :
                                     this.layoutDoc.hideLabels ? (null) :
                                         <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={i}
-                                            style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}>
-                                            {markerDoc(m, this.labelScript)}
+                                            style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}
+                                            onClick={e => { this.playFrom(NumCast(m.audioStart)); e.stopPropagation(); }}>
+                                            {markerDoc(m, this.labelScript,
+                                                playheadWidth + NumCast(m.audioStart) / this.audioDuration * timelineContentWidth,
+                                                .1 * this.props.PanelHeight(),
+                                                10, 10)}
                                         </div>;
                             })}
-                            {DocListCast(this.dataDoc.links).map((l, i) => {
-                                const { la1, la2, linkTime } = this.getLinkData(l);
-                                let startTime = linkTime;
-                                if (la2.audioStart && !la2.audioEnd) {
-                                    startTime = NumCast(la2.audioStart);
-                                }
-
-                                return !linkTime ? (null) :
-                                    <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${startTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}>
-                                        <DocumentView {...this.props}
-                                            Document={l}
-                                            rootSelected={returnFalse}
-                                            ContainingCollectionDoc={this.props.Document}
-                                            parentActive={returnTrue}
-                                            bringToFront={emptyFunction}
-                                            styleProvider={AudioBox.audioStyleProvider}
-                                            LayoutTemplate={undefined}
-                                            LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
-                                        />
-                                        <div key={i} className={`audiobox-marker`} onPointerEnter={() => Doc.linkFollowHighlight(la1)}
-                                            onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(startTime); e.stopPropagation(); e.preventDefault(); } }} />
-                                    </div>;
-                            })}
                             {this._visible ? this.selectionContainer : null}
 
                             <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index e7a94580a..2cac2d0b8 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -160,6 +160,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             DocumentLinksButton.AnnotationUri = undefined;
             //!this.props.StartLink 
         } else if (startLink !== endLink) {
+            endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
+            startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
             const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true);
             // this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks ().  see note above in onLInkButtonMoved
             if (endLinkView) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4c53ab07d..b44c15a78 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -29,6 +29,7 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
+import { InkingStroke } from "../InkingStroke";
 import { InkStrokeProperties } from '../InkStrokeProperties';
 import { StyleLayers, StyleProp } from "../StyleProvider";
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
@@ -40,7 +41,6 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { PresBox } from './PresBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
-import { InkingStroke } from "../InkingStroke";
 
 export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
 export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
@@ -52,6 +52,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    setContentView?: (view: { getAnchor: () => Doc }) => any,
     CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
     PanelWidth: () => number;
     PanelHeight: () => number;
@@ -691,9 +692,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
     makeLink = () => this.props.DocumentView._link; // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
+    setContentView = (view: { getAnchor: () => Doc }) => this._componentView = view;
     @observable contentsActive: () => boolean = returnFalse;
     @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive;
-
+    _componentView: { getAnchor: () => Doc } | undefined;
     @computed get contents() {
         TraceMobx();
         return <div className="documentView-contentsView"
@@ -702,6 +704,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
+                setContentView={this.setContentView}
                 scaling={this.contentScaling}
                 PanelHeight={this.panelHeight}
                 contentsActive={this.setContentsActive}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 393ba07e6..b0e7f4ce5 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -413,7 +413,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
                 borderRadius
             }} >
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 renderDepth={this.props.renderDepth + 1}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 CollectionView={undefined}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 71c5d67d8..c2c159e4b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -428,7 +428,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 borderRadius
             }} >
             <div className="videoBox-viewer" >
-                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                     forceScaling={true}
                     fieldKey={this.annotationKey}
                     isAnnotationOverlay={true}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8e824a447..7fb14b0cc 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -689,7 +689,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         height: NumCast(this.scrollHeight, 50),
                         pointerEvents: inactiveLayer ? "none" : undefined
                     }}>
-                        <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                        <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                             renderDepth={this.props.renderDepth + 1}
                             CollectionView={undefined}
                             fieldKey={this.annotationKey}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 02f7ada96..8700a5739 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -713,7 +713,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
                 transform: `scale(${this._zoomed})`
             }}>
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
                 setPreviewCursor={this.setPreviewCursor}
-- 
cgit v1.2.3-70-g09d2


From ad0cad318eb04199a0a68752b74374ff8f66a3be Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Jan 2021 11:24:12 -0500
Subject: fixed rendering of labels in audioBox's to use standard dash-style
 provider stuff.  added double click to play

---
 src/client/views/StyleProvider.tsx      |  3 ++
 src/client/views/nodes/AudioBox.scss    |  2 -
 src/client/views/nodes/AudioBox.tsx     | 81 +++++++++++++++++++--------------
 src/client/views/nodes/DocumentView.tsx |  4 +-
 4 files changed, 53 insertions(+), 37 deletions(-)

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

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8560f38cb..1d822439a 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -138,6 +138,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
                     return StrCast(doc?.boxShadow,
                         isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
                             `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+
+                case DocumentType.LABEL:
+                    if (doc?.audioStart !== undefined) return "black 2px 2px 1px";
                 default:
                     return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
                         props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 029082112..9a8ad71c8 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -283,9 +283,7 @@
                     width: 10px;
                     height: 90%;
                     top: 2.5%;
-                    background: rgba(128, 128, 128, 0.18);
                     border-radius: 5px;
-                    box-shadow: black 2px 2px 1px;
 
                     .audiobox-marker {
                         position: relative;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index f4f32b900..141f90e83 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -50,6 +50,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     static Instance: AudioBox;
     static RangeScript: ScriptField;
     static LabelScript: ScriptField;
+    static RangePlayScript: ScriptField;
+    static LabelPlayScript: ScriptField;
 
     // _linkPlayDisposer: IReactionDisposer | undefined;
     // _reactionDisposer: IReactionDisposer | undefined;
@@ -94,8 +96,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         AudioBox.Instance = this;
 
         // onClick play scripts
-        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
-        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.RangePlayScript = AudioBox.RangePlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.LabelPlayScript = AudioBox.LabelPlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
     }
 
     getLinkData(l: Doc) {
@@ -233,8 +237,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     @action
     playOnClick = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
         DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false);
-        if (this.layoutDoc.playOnClick) this.playFrom(seekTimeInSeconds, endTime);
-        else this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds);
+        this.playFrom(seekTimeInSeconds, endTime);
+    }
+
+    // play back the audio from time
+    @action
+    clickMarker = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
+        if (this.layoutDoc.playOnClick) this.playOnClick(anchorDoc, seekTimeInSeconds, endTime);
+        else {
+            DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false);
+            this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds);
+        }
     }
     // play back the audio from time
     @action
@@ -523,7 +536,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             color={"darkblue"}
             height={this._waveHeight}
             barWidth={0.1}
-            // pos={this.layoutDoc._currentTimecode} need to correctly resize parent to make this work (not very necessary for function)
             pos={this.audioDuration}
             duration={this.audioDuration}
             peaks={audioBuckets.length === AudioBox.NUMBER_OF_BUCKETS ? audioBuckets : undefined}
@@ -545,39 +557,40 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             );
     }
 
-    rangeScript = () => AudioBox.RangeScript;
-    labelScript = () => AudioBox.LabelScript;
-    audioStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => {
-        if (property === StyleProp.BackgroundColor) return "transparent";
-        if (property === StyleProp.PointerEvents) return "none";
-        return this.props.styleProvider?.(doc, props, property);
+    rangeClickScript = () => AudioBox.RangeScript;
+    labelClickScript = () => AudioBox.LabelScript;
+    rangePlayScript = () => AudioBox.RangePlayScript;
+    labelPlayScript = () => AudioBox.LabelPlayScript;
+    renderMarker = (mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) => {
+        return <DocumentView {...this.props}
+            Document={mark}
+            PanelWidth={() => width}
+            PanelHeight={() => height}
+            focus={() => this.playLink(mark)}
+            rootSelected={returnFalse}
+            LayoutTemplate={undefined}
+            ContainingCollectionDoc={this.props.Document}
+            removeDocument={this.removeDocument}
+            ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3)}
+            parentActive={returnTrue}
+            onClick={script}
+            onDoubleClick={this.layoutDoc.playOnClick ? undefined : doublescript}
+            ignoreAutoHeight={false}
+            bringToFront={emptyFunction}
+            scriptContext={this} />;
     }
     render() {
         const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : "";
         this._first = true;  // for indicating the first marker that is rendered
-        const markerDoc = (mark: Doc, script: undefined | (() => ScriptField), x?: number, y?: number, width?: number, height?: number) => {
-            return <DocumentView {...this.props}
-                Document={mark}
-                PanelWidth={width ? () => width : this.props.PanelWidth}
-                PanelHeight={height ? () => height : this.props.PanelHeight}
-                focus={() => this.playLink(mark)}
-                pointerEvents={"all"}
-                rootSelected={returnFalse}
-                LayoutTemplate={undefined}
-                ContainingCollectionDoc={this.props.Document}
-                removeDocument={this.removeDocument}
-                ScreenToLocalTransform={x && y ? () => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3) : this.props.ScreenToLocalTransform}
-                parentActive={returnTrue}
-                onClick={script}
-                ignoreAutoHeight={false}
-                bringToFront={emptyFunction}
-                scriptContext={this} />;
-        };
+
         const heightPercent = 80;
         const playheadWidth = 30;
         const timelineContentWidth = this.props.PanelWidth() - playheadWidth;
         const timelineContentHeight = (this.props.PanelHeight() * heightPercent / 100) * heightPercent / 100; // panelHeight * heightPercent is player height.   * heightPercent is timeline height (as per css inline)
-        return <div className="audiobox-container" onContextMenu={this.specificContextMenu} onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined} style={{ pointerEvents: !interactive ? "none" : undefined }}>
+        return <div className="audiobox-container"
+            onContextMenu={this.specificContextMenu}
+            onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
+            style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined }}>
             {!this.path ?
                 <div className="audiobox-buttons">
                     <div className="audiobox-dictation" onClick={this.onFile}>
@@ -586,10 +599,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                     {this.audioState === "recording" || this.audioState === "paused" ?
                         <div className="recording" onClick={e => e.stopPropagation()}>
                             <div className="buttons" onClick={this.recordClick}>
-                                <FontAwesomeIcon style={{ width: "100%" }} icon={"stop"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+                                <FontAwesomeIcon icon={"stop"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
                             </div>
                             <div className="buttons" onClick={this._paused ? this.recordPlay : this.recordPause}>
-                                <FontAwesomeIcon style={{ width: "100%" }} icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+                                <FontAwesomeIcon icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
                             </div>
                             <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
                         </div>
@@ -633,7 +646,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                             }}
                                             onClick={e => { this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} >
                                             <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)} />
-                                            {markerDoc(m, this.rangeScript,
+                                            {this.renderMarker(m, this.rangeClickScript, this.rangePlayScript,
                                                 playheadWidth + NumCast(m.audioStart) / this.audioDuration * timelineContentWidth,
                                                 .1 * this.props.PanelHeight() + isOverlap / (this.dataDoc.markerAmount + 1) * timelineContentHeight,
                                                 timelineContentWidth * (NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration,
@@ -645,7 +658,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                         <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={i}
                                             style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}
                                             onClick={e => { this.playFrom(NumCast(m.audioStart)); e.stopPropagation(); }}>
-                                            {markerDoc(m, this.labelScript,
+                                            {this.renderMarker(m, this.labelClickScript, this.labelPlayScript,
                                                 playheadWidth + NumCast(m.audioStart) / this.audioDuration * timelineContentWidth,
                                                 .1 * this.props.PanelHeight(),
                                                 10, 10)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b44c15a78..98995d040 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -379,14 +379,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         const func = () => this.onDoubleClickHandler.script.run({
                             this: this.layoutDoc,
                             self: this.rootDoc,
+                            scriptContext: this.props.scriptContext,
                             thisContainer: this.props.ContainingCollectionDoc,
+                            documentView: this.props.DocumentView,
                             shiftKey: e.shiftKey
                         }, console.log);
                         undoBatch(func)();
                     } else if (!Doc.IsSystem(this.props.Document)) {
                         if (this.props.Document.type === DocumentType.INK) {
                             InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
-                        } else {
+                        } else if (this.props.Document.type !== DocumentType.LABEL) {
                             UndoManager.RunInBatch(() => {
                                 const fullScreenDoc = Cast(this.props.Document._fullScreenView, Doc, null) || this.props.Document;
                                 this.props.addDocTab(fullScreenDoc, "add");
-- 
cgit v1.2.3-70-g09d2


From 4eb2de2c53280104fc809bbd387ed97460312e69 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Jan 2021 17:13:57 -0500
Subject: simplified links to audio to always have a target Doc anchor.  
 updated auto links to audio when recording.  fixed making text selection
 links.

---
 src/client/documents/Documents.ts                  | 12 +--
 src/client/util/DragManager.ts                     |  8 +-
 src/client/views/nodes/AudioBox.tsx                | 88 +++++++---------------
 src/client/views/nodes/DocumentView.tsx            | 17 +----
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 5 files changed, 41 insertions(+), 87 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c457fb722..b32cbd3d0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,4 +1,4 @@
-import { runInAction, action } from "mobx";
+import { action, runInAction } from "mobx";
 import { basename, extname } from "path";
 import { DateField } from "../../fields/DateField";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
@@ -42,19 +42,19 @@ import { ImageBox } from "../views/nodes/ImageBox";
 import { KeyValueBox } from "../views/nodes/KeyValueBox";
 import { LabelBox } from "../views/nodes/LabelBox";
 import { LinkBox } from "../views/nodes/LinkBox";
+import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
 import { PDFBox } from "../views/nodes/PDFBox";
 import { PresBox } from "../views/nodes/PresBox";
 import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
 import { ScriptingBox } from "../views/nodes/ScriptingBox";
 import { SliderBox } from "../views/nodes/SliderBox";
+import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 import { VideoBox } from "../views/nodes/VideoBox";
 import { WebBox } from "../views/nodes/WebBox";
 import { PresElementBox } from "../views/presentationview/PresElementBox";
 import { SearchBox } from "../views/search/SearchBox";
 import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
 const path = require('path');
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
@@ -610,7 +610,7 @@ export namespace Docs {
             proto.links = ComputedField.MakeFunction("links(self)");
 
             viewDoc.author = Doc.CurrentUserEmail;
-            viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
+            viewDoc.type !== DocumentType.LINK && viewDoc.type !== DocumentType.LABEL && DocUtils.MakeLinkToActiveAudio(viewDoc);
 
             viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
             viewDoc["acl-Override"] = dataDoc["acl-Override"] = "None";
@@ -1008,10 +1008,10 @@ export namespace DocUtils {
         });
     }
 
-    export let ActiveRecordings: Doc[] = [];
+    export let ActiveRecordings: AudioBox[] = [];
 
     export function MakeLinkToActiveAudio(doc: Doc) {
-        DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
+        DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d.getAnchor() || d.props.Document }, "audio link", "audio timeline"));
     }
 
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d24348746..52ccfda74 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -529,15 +529,16 @@ export namespace DragManager {
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
-            dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
-            options?.dragComplete?.(new DragCompleteEvent(false, dragData));
+            const complete = new DragCompleteEvent(false, dragData);
+            dispatchDrag(eles, e, complete, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
+            options?.dragComplete?.(complete);
             endDrag();
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
     }
 
-    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any },
+    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, complete: DragCompleteEvent,
         xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
         const removed = dragEles.map(dragEle => {
             const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height, o: dragEle.style.overflow };
@@ -558,7 +559,6 @@ export namespace DragManager {
         });
         const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
         if (target) {
-            const complete = new DragCompleteEvent(false, dragData);
             target.dispatchEvent(
                 new CustomEvent<DropEvent>("dashPreDrop", {
                     bubbles: true,
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 21dfec888..9f8bdcd57 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -30,7 +30,6 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
 import { LinkDocPreview } from "./LinkDocPreview";
 import { computedFn } from "mobx-utils";
-import { ObservableValue } from "mobx/lib/internal";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -46,7 +45,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
     public static Enabled = false;
     public static NUMBER_OF_BUCKETS = 100;
-    static playheadWidth = 30; // width of playhead 
+    static playheadWidth = 30; // width of playhead
     static heightPercent = 80; // height of timeline in percent of height of audioBox.
 
     static Instance: AudioBox;
@@ -104,60 +103,34 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        let linkTime = NumCast(l.anchor2_timecode);
+        let linkTime = NumCast(la2.audioStart, NumCast(la1.audioStart));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
-            linkTime = NumCast(l.anchor1_timecode);
         }
         return { la1, la2, linkTime };
     }
 
     componentWillUnmount() {
-        this._disposers.reaction?.();
-        this._disposers.linkPlay?.();
-        this._disposers.scrubbing?.();
-        this._disposers.audioStart?.();
+        Object.values(this._disposers).forEach(disposer => disposer?.());
+        const ind = DocUtils.ActiveRecordings.indexOf(this);
+        ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
     }
 
     getAnchor = () => {
-        return this._ele?.currentTime ? this.createMarker(this._ele?.currentTime) : this.rootDoc;
+        const time = this._ele?.currentTime || Cast(this.props.Document._currentTimecode, "number", null) || (this.audioState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        return time ? this.createMarker(time) : this.rootDoc;
     }
+
     @action
     componentDidMount() {
         if (!this.dataDoc.markerAmount) {
             this.dataDoc.markerAmount = 0;
         }
-        this.props.setContentView?.(this);
+        this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
 
         this.audioState = this.path ? "paused" : undefined;
-        this._disposers.linkPlay = reaction(() => this.layoutDoc.scrollToLinkID,
-            scrollLinkId => {
-                if (scrollLinkId) {
-                    this.links.filter(l => l[Id] === scrollLinkId).map(l => {
-                        const { linkTime } = this.getLinkData(l);
-                        setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250);
-                    });
-                    Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
-                }
-            }, { fireImmediately: true });
-
-        // for play when link is selected
-        this._disposers.reaction = reaction(() => SelectionManager.Views(),
-            selected => {
-                const sel = selected.length ? selected[0].props.Document : undefined;
-                let link;
-                sel && this.links.forEach(l => {
-                    if (l.anchor1 === sel || l.anchor2 === sel && !sel.audioStart) {
-                        link = this.playLink(sel);
-                    }
-                });
-                // for links created during recording 
-                if (!link) {
-                    this.layoutDoc.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
-                    this.layoutDoc.playOnSelect && this.recordingStart && !sel && this.pause();
-                }
-            });
+
         this._disposers.scrubbing = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
 
         this._disposers.audioStart = reaction(
@@ -183,27 +156,18 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     }
 
     playLink = (doc: Doc) => {
-        let link = false;
-        !Doc.AreProtosEqual(doc, this.props.Document) && this.links.forEach(l => {
-            if (l.anchor1 === doc || l.anchor2 === doc) {
-                const { la1, la2, linkTime } = this.getLinkData(l);
-                let startTime = linkTime;
-                if (la2.audioStart) startTime = NumCast(la2.audioStart);
-                if (la1.audioStart) startTime = NumCast(la1.audioStart);
-
-                let endTime;
-                if (la1.audioEnd) endTime = NumCast(la1.audioEnd);
-                if (la2.audioEnd) endTime = NumCast(la2.audioEnd);
-
-                if (startTime) {
-                    link = true;
-                    this.layoutDoc.playOnSelect && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
-                }
+        console.log(doc);
+        this.links.filter(l => l.anchor1 === doc || l.anchor2 === doc).forEach(l => {
+            const { la1, la2 } = this.getLinkData(l);
+            const startTime = NumCast(la1.audioStart, NumCast(la2.audioStart, null));
+            const endTime = NumCast(la1.audioEnd, NumCast(la2.audioEnd, null));
+            if (startTime !== undefined) {
+                this.layoutDoc.playOnSelect && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
             }
         });
-
-        this.layoutDoc.playOnSelect && Doc.AreProtosEqual(doc, this.props.Document) && this.pause();
-        return link;
+        if (doc.annotationOn === this.rootDoc) {
+            this.playFrom(NumCast(doc.audioStart), Cast(doc.audioEnd, "number", null));
+        }
     }
 
     // for updating the timecode
@@ -227,7 +191,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this.audioState = "paused";
     });
 
-    // play audio for documents created during recording 
+    // play audio for documents created during recording
     playFromTime = (absoluteTime: number) => {
         this.recordingStart && this.playFrom((absoluteTime - this.recordingStart) / 1000);
     }
@@ -292,7 +256,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._stream = await navigator.mediaDevices.getUserMedia({ audio: true });
         this._recorder = new MediaRecorder(this._stream);
         this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-        DocUtils.ActiveRecordings.push(this.props.Document);
+        DocUtils.ActiveRecordings.push(this);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
             if (!(result instanceof Error)) {
@@ -315,14 +279,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
-    // stops the recording 
+    // stops the recording
     stopRecording = action(() => {
         this._recorder.stop();
         this._recorder = undefined;
         this.dataDoc.duration = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
         this.audioState = "paused";
         this._stream?.getAudioTracks()[0].stop();
-        const ind = DocUtils.ActiveRecordings.indexOf(this.props.Document);
+        const ind = DocUtils.ActiveRecordings.indexOf(this);
         ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
     });
 
@@ -413,7 +377,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
             wasPaused && this.pause();
 
-            this.props.select(false)
+            this.props.select(false);
             const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration;
             this._markerStart = this._markerEnd = toTimeline(e.clientX - rect.x);
             setupMoveUpEvents(this, e,
@@ -568,7 +532,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     rangePlayScript = () => AudioBox.RangePlayScript;
     labelPlayScript = () => AudioBox.LabelPlayScript;
     renderInner = computedFn(function (this: AudioBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
-        let marker = observable({ view: undefined as any });
+        const marker = observable({ view: undefined as any });
         return {
             marker, view: <DocumentView key="view" {...this.props} ref={action((r: DocumentView | null) => marker.view = r)}
                 Document={mark}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98995d040..131e33e2a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -52,7 +52,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
-    setContentView?: (view: { getAnchor: () => Doc }) => any,
+    setContentView?: (view: { getAnchor: () => Doc }) => any;
     CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
     PanelWidth: () => number;
     PanelHeight: () => number;
@@ -553,7 +553,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const linkSource = de.complete.annoDragData ? de.complete.annoDragData.annotationDocument : de.complete.linkDragData ? de.complete.linkDragData.linkSourceDocument : undefined;
         if (linkSource && linkSource !== this.props.Document) {
             e.stopPropagation();
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: linkSource }, { doc: this.props.Document }, "link", undefined, undefined, undefined, [de.x, de.y]);
+            de.complete.linkDocument = DocUtils.MakeLink({ doc: linkSource }, { doc: this._componentView?.getAnchor() || this.rootDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
         }
     }
 
@@ -693,7 +693,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    makeLink = () => this.props.DocumentView._link; // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
+    makeLink = () => this.props.DocumentView.LinkBeingCreated; // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
     setContentView = (view: { getAnchor: () => Doc }) => this._componentView = view;
     @observable contentsActive: () => boolean = returnFalse;
     @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive;
@@ -722,15 +722,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         </div>;
     }
 
-    // used to decide whether a link anchor view should be created or not.
-    // if it's a temporal link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
-    // would be good to generalize this some way.
-    isNonTemporalLink = (linkDoc: Doc) => {
-        const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
-        const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1_timecode : linkDoc.anchor2_timecode;
-        return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
-    }
-
     @undoBatch
     hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true)
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
@@ -745,7 +736,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
         if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
 
-        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink(d));
+        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden);
         return filtered.map((d, i) =>
             <div className="documentView-anchorCont" key={i + 1}>
                 <DocumentView {...this.props}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2b9910dfb..c129d0204 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -320,12 +320,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             DocListCast(this.dataDoc.links).map((l, i) => {
                 let la1 = l.anchor1 as Doc;
                 let la2 = l.anchor2 as Doc;
-                this._linkTime = NumCast(l.anchor2_timecode);
+                this._linkTime = NumCast(la1.audioStart, NumCast(la2.audioStart));
                 audioState = la2.audioState;
                 if (Doc.AreProtosEqual(la2, this.dataDoc)) {
                     la1 = l.anchor2 as Doc;
                     la2 = l.anchor1 as Doc;
-                    this._linkTime = NumCast(l.anchor1_timecode);
                     audioState = la1.audioState;
                 }
             });
-- 
cgit v1.2.3-70-g09d2


From ba3bcbbf3f0ce8769acb6e84ac3aa8ac3a5c9694 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 15 Jan 2021 15:27:16 -0500
Subject: fixed more issues with audio Box - simplified decorations when
 selected.  fixed dragging when not selected.  added esc to stop dragging.

---
 src/client/views/DocumentDecorations.tsx  |  54 ++++++------
 src/client/views/GlobalKeyHandler.ts      |   4 +
 src/client/views/InkStrokeProperties.ts   |   2 +-
 src/client/views/collections/TreeView.tsx |   5 +-
 src/client/views/nodes/AudioBox.scss      |   1 -
 src/client/views/nodes/AudioBox.tsx       | 140 +++++++++++-------------------
 src/client/views/nodes/DocumentView.tsx   |   2 +-
 7 files changed, 91 insertions(+), 117 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c2de78d09..e1b9fe8aa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -569,7 +569,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                     <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
                 </div></Tooltip>);
 
-        const openIcon = !canOpen ? (null) : <Tooltip title={<div className="dash-tooltip">Open in Tab (ctrl: as alias, shift: in new collection)</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
+        const openIcon = !canOpen ? (null) : <Tooltip key="open" title={<div className="dash-tooltip">Open in Tab (ctrl: as alias, shift: in new collection)</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
             {SelectionManager.Views().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
         </div>
         </Tooltip>;
@@ -613,31 +613,35 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                 }}>
                     {closeIcon}
                     {bounds.r - bounds.x < 100 ? null : titleArea}
-                    {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
-                        <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
-                            <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
-                                <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
-                            </div>
-                        </Tooltip>}
-                    {openIcon}
-                    <div className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-centerCont"></div>
-                    <div className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    <div className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                        <Tooltip title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
-                            <div className="documentDecorations-levelSelector"
-                                onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
-                                <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
-                            </div></Tooltip>}
-                    <div className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+                    {(seldoc.rootDoc.annotationOn as Doc)?.type === DocumentType.AUDIO ? (null) :
+                        <>
+                            {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
+                                <Tooltip key="i" title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
+                                    <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
+                                        <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
+                                    </div>
+                                </Tooltip>}
+                            {openIcon}
+                            <div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="l" className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="c" className="documentDecorations-centerCont"></div>
+                            <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+
+                            {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+                                <Tooltip key="level" title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
+                                    <div className="documentDecorations-levelSelector"
+                                        onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
+                                        <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
+                                    </div></Tooltip>}
+                        </>
+                    }
+                    <div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
                         onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div>
-
                 </div >
                 {seldoc?.Document.type === DocumentType.FONTICON ? (null) : <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
                     <DocumentButtonBar views={SelectionManager.Views} />
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c38842c4f..8a07c5321 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -27,6 +27,7 @@ import { SnappingManager } from "../util/SnappingManager";
 import { SearchBox } from "./search/SearchBox";
 import { random } from "lodash";
 import { DocumentView } from "./nodes/DocumentView";
+import { AudioBox } from "./nodes/AudioBox";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -121,6 +122,9 @@ export class KeyManager {
                     DragManager.AbortDrag();
                 } else if (CollectionDockingView.Instance.HasFullScreen) {
                     CollectionDockingView.Instance.CloseFullScreen();
+                } else if (AudioBox.SelectingRegion) {
+                    AudioBox.SelectingRegion = undefined;
+                    doDeselect = false;
                 } else {
                     doDeselect = !ContextMenu.Instance.closeMenu();
                 }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index fcc6b9da0..ce7f3d8d9 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -220,7 +220,7 @@ export class InkStrokeProperties {
                                     ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
                                 ) ?
                                     { X: ink[i].X - xDiff, Y: ink[i].Y - yDiff } :
-                                    { X: ink[i].X, Y: ink[i].Y })
+                                    { X: ink[i].X, Y: ink[i].Y });
                         }
                         const oldx = doc.x;
                         const oldy = doc.y;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a61a9e43c..c4934cf45 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -379,7 +379,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
         if (["links", "annotations", this.fieldKey].includes(expandKey)) {
-            const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
+            const key = expandKey === "annotations" ? this.fieldKey + "-annotations" : expandKey;
+            const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
             const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                 // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
                 // then the modification would be done here
@@ -391,7 +392,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1);
                     docs.forEach((d, i) => d.zIndex = i);
                 }
-                const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+                const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 added && (doc.context = this.doc.context);
                 return added;
             };
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 36cc22d79..93cdbbb09 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -4,7 +4,6 @@
     height: 100%;
     position: inherit;
     display: flex;
-    pointer-events: all;
     position: relative;
     cursor: default;
 
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 9f8bdcd57..c3c876b75 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -3,17 +3,17 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import axios from "axios";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
+import { computedFn } from "mobx-utils";
 import Waveform from "react-audio-waveform";
 import { DateField } from "../../../fields/DateField";
 import { Doc, DocListCast, Opt } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
 import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, DateCast, NumCast } from "../../../fields/Types";
+import { Cast, NumCast } from "../../../fields/Types";
 import { AudioField, nullAudio } from "../../../fields/URLField";
-import { emptyFunction, formatTime, numberRange, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, formatTime, numberRange, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -29,7 +29,6 @@ import { DocumentView } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
 import { LinkDocPreview } from "./LinkDocPreview";
-import { computedFn } from "mobx-utils";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -47,7 +46,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     public static NUMBER_OF_BUCKETS = 100;
     static playheadWidth = 30; // width of playhead
     static heightPercent = 80; // height of timeline in percent of height of audioBox.
-
     static Instance: AudioBox;
     static RangeScript: ScriptField;
     static LabelScript: ScriptField;
@@ -65,29 +63,27 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     _start: number = 0;
     _hold: boolean = false;
     _left: boolean = false;
-    _first: boolean = false;
     _dragging = false;
     _play: any = null;
-
-    _count: Array<any> = [];
     _audioRef = React.createRef<HTMLDivElement>();
     _timeline: Opt<HTMLDivElement>;
-    _duration = 0;
     _markerStart: number = 0;
     _currMarker: any;
 
-    @observable _selectRegionVisible: boolean = false;
+    @observable static SelectingRegion: AudioBox | undefined = undefined;
+    @observable static _scrubTime = 0;
     @observable _markerEnd: number = 0;
     @observable _position: number = 0;
     @observable _waveHeight: Opt<number> = this.layoutDoc._height;
-    @observable private _paused: boolean = false;
-    @observable private static _scrubTime = 0;
+    @observable _paused: boolean = false;
     @computed get audioState(): undefined | "recording" | "paused" | "playing" { return this.dataDoc.audioState as (undefined | "recording" | "paused" | "playing"); }
     set audioState(value) { this.dataDoc.audioState = value; }
     public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; AudioBox._scrubTime = timeInMillisFrom1970; });
     @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
     @computed get audioDuration() { return NumCast(this.dataDoc.duration); }
-    async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); }
+    @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
+    @computed get links() { return DocListCast(this.dataDoc.links); }
+    @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct time
 
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
@@ -103,7 +99,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        let linkTime = NumCast(la2.audioStart, NumCast(la1.audioStart));
+        const linkTime = NumCast(la2.audioStart, NumCast(la1.audioStart));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -118,15 +114,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     }
 
     getAnchor = () => {
-        const time = this._ele?.currentTime || Cast(this.props.Document._currentTimecode, "number", null) || (this.audioState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-        return time ? this.createMarker(time) : this.rootDoc;
+        return this.createMarker(this._ele?.currentTime || Cast(this.props.Document._currentTimecode, "number", null) || (this.audioState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined));
     }
 
     @action
     componentDidMount() {
-        if (!this.dataDoc.markerAmount) {
-            this.dataDoc.markerAmount = 0;
-        }
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
 
         this.audioState = this.path ? "paused" : undefined;
@@ -156,7 +148,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     }
 
     playLink = (doc: Doc) => {
-        console.log(doc);
         this.links.filter(l => l.anchor1 === doc || l.anchor2 === doc).forEach(l => {
             const { la1, la2 } = this.getLinkData(l);
             const startTime = NumCast(la1.audioStart, NumCast(la2.audioStart, null));
@@ -165,9 +156,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 this.layoutDoc.playOnSelect && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
             }
         });
-        if (doc.annotationOn === this.rootDoc) {
-            this.playFrom(NumCast(doc.audioStart), Cast(doc.audioEnd, "number", null));
-        }
+        doc.annotationOn === this.rootDoc && this.playFrom(NumCast(doc.audioStart), Cast(doc.audioEnd, "number", null));
     }
 
     // for updating the timecode
@@ -216,7 +205,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     @action
     playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
         clearTimeout(this._play);
-        this._duration = endTime - seekTimeInSeconds;
         if (Number.isNaN(this._ele?.duration)) {
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
         } else if (this._ele && AudioBox.Enabled) {
@@ -231,7 +219,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 this._ele.play();
                 runInAction(() => this.audioState = "playing");
                 if (endTime !== this.audioDuration) {
-                    this._play = setTimeout(() => this.pause(), (this._duration) * 1000); // use setTimeout to play a specific duration
+                    this._play = setTimeout(() => this.pause(), (endTime - seekTimeInSeconds) * 1000); // use setTimeout to play a specific duration
                 }
             } else {
                 this.pause();
@@ -350,7 +338,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._paused = true;
         this._recorder.pause();
         e.stopPropagation();
-
     }
 
     // continue the recording
@@ -360,29 +347,22 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         this._paused = false;
         this._recorder.resume();
         e.stopPropagation();
-
-    }
-
-    // return the total time paused to update the correct time
-    @computed get pauseTime() {
-        return this._pauseEnd - this._pauseStart;
     }
 
     // starting the drag event for marker resizing
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
         const rect = this._timeline?.getBoundingClientRect();// (e.target as any).getBoundingClientRect();
-        if (rect && e.target !== this._audioRef.current) {
+        if (rect && e.target !== this._audioRef.current && this.active()) {
             const wasPaused = this.audioState === "paused";
             this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
             wasPaused && this.pause();
 
-            this.props.select(false);
             const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration;
             this._markerStart = this._markerEnd = toTimeline(e.clientX - rect.x);
+            AudioBox.SelectingRegion = this;
             setupMoveUpEvents(this, e,
                 action(e => {
-                    this._selectRegionVisible = true;
                     this._markerEnd = toTimeline(e.clientX - rect.x);
                     if (this._markerEnd < this._markerStart) {
                         const tmp = this._markerStart;
@@ -392,16 +372,20 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                     return false;
                 }),
                 action((e, movement) => {
-                    (Math.abs(movement[0]) > 15) && this.createMarker(this._markerStart, toTimeline(e.clientX - rect.x));
-                    this._selectRegionVisible = false;
+                    AudioBox.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createMarker(this._markerStart, toTimeline(e.clientX - rect.x));
+                    AudioBox.SelectingRegion = undefined;
                 }),
-                e => e.shiftKey && this.createMarker(this._ele!.currentTime)
-            );
+                e => {
+                    this.props.select(false);
+                    e.shiftKey && this.createMarker(this._ele!.currentTime);
+                }
+                , this.props.isSelected(true) || this._isChildActive);
         }
     }
 
     @action
-    createMarker(audioStart: number, audioEnd?: number) {
+    createMarker(audioStart?: number, audioEnd?: number) {
+        if (audioStart === undefined) return this.rootDoc;
         const marker = Docs.Create.LabelDocument({
             title: ComputedField.MakeFunction(`"#" + formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any,
             useLinkSmallAnchor: true, hideLinkButton: true, audioStart, audioEnd, _showSidebar: false,
@@ -448,56 +432,35 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd;
     }
 
-    // instantiates a new array of size NUMBER_OF_BUCKETS for marker layout
-    markers = () => {
-        const increment = this.audioDuration / AudioBox.NUMBER_OF_BUCKETS;
-        this._count = [];
-        for (let i = 0; i < AudioBox.NUMBER_OF_BUCKETS; i++) {
-            this._count.push([increment * i, 0]);
-        }
-    }
-
     // makes sure no markers overlaps each other by setting the correct position and width
-    isOverlap = (m: any) => {
-        if (this._first) {
-            this._first = false;
-            this.markers();
-        }
-        let max = 0;
+    getLevel = (m: any, placed: { audioStart: number, audioEnd: number, level: number }[]) => {
         const timelineContentWidth = this.props.PanelWidth() - AudioBox.playheadWidth;
-        const audioEnd = m.audioEnd === undefined ? m.audioStart + 10 / timelineContentWidth * this.audioDuration : m.audioEnd;
-        for (let i = 0; i < AudioBox.NUMBER_OF_BUCKETS; i++) {
-            if (this._count[i][0] >= m.audioStart && this._count[i][0] <= audioEnd) {
-                this._count[i][1]++;
-
-                if (this._count[i][1] > max) {
-                    max = this._count[i][1];
-                }
-            }
-        }
-
-        for (let i = 0; i < AudioBox.NUMBER_OF_BUCKETS; i++) {
-            if (this._count[i][0] >= m.audioStart && this._count[i][0] <= audioEnd) {
-                this._count[i][1] = max;
+        const x1 = m.audioStart;
+        const x2 = m.audioEnd === undefined ? m.audioStart + 10 / timelineContentWidth * this.audioDuration : m.audioEnd;
+        let max = 0;
+        const overlappedLevels = new Set(placed.map(p => {
+            const y1 = p.audioStart;
+            const y2 = p.audioEnd;
+            if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) ||
+                (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
+                max = Math.max(max, p.level);
+                return p.level;
             }
-        }
+        }));
+        let level = max + 1;
+        for (let j = max; j >= 0; j--) !overlappedLevels.has(j) && (level = j);
 
-        if (this.dataDoc.markerAmount < max) {
-            this.dataDoc.markerAmount = max;
-        }
-        return max - 1;
+        placed.push({ audioStart: x1, audioEnd: x2, level });
+        return level;
     }
 
     @computed get selectionContainer() {
-        return !this._selectRegionVisible ? (null) : <div className="audiobox-container" style={{
+        return AudioBox.SelectingRegion !== this ? (null) : <div className="audiobox-container" style={{
             left: `${NumCast(this._markerStart) / this.audioDuration * 100}%`,
             width: `${Math.abs(this._markerStart - this._markerEnd) / this.audioDuration * 100}%`, height: "100%", top: "0%"
         }} />;
     }
 
-    @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-
     // returns the audio waveform
     @computed get waveform() {
         const audioBuckets = Cast(this.dataDoc.audioBuckets, listSpec("number"), []);
@@ -544,7 +507,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 ContainingCollectionDoc={this.props.Document}
                 removeDocument={this.removeDocument}
                 ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3)}
-                parentActive={returnTrue}
+                parentActive={(out) => this.props.isSelected(out) || this._isChildActive}
+                whenActiveChanged={action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive))}
                 onClick={script}
                 onDoubleClick={this.layoutDoc.playOnClick ? undefined : doublescript}
                 ignoreAutoHeight={false}
@@ -563,12 +527,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 </>}
         </>;
     });
+
     render() {
         const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : "";
-        this._first = true;  // for indicating the first marker that is rendered
-
         const timelineContentWidth = this.props.PanelWidth() - AudioBox.playheadWidth;
         const timelineContentHeight = (this.props.PanelHeight() * AudioBox.heightPercent / 100) * AudioBox.heightPercent / 100; // panelHeight * heightPercent is player height.   * heightPercent is timeline height (as per css inline)
+        const overlaps: { audioStart: number, audioEnd: number, level: number }[] = [];
+        const drawMarkers = this.markerDocs.map((m, i) => ({ level: this.getLevel(m, overlaps), marker: m }));
+        const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 1;
         return <div className="audiobox-container"
             onContextMenu={this.specificContextMenu}
             onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
@@ -593,7 +559,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                             RECORD
                         </button>}
                 </div> :
-                <div className="audiobox-controls" >
+                <div className="audiobox-controls" style={{ pointerEvents: this._isChildActive || this.active() ? "all" : "none" }} >
                     <div className="audiobox-dictation" />
                     <div className="audiobox-player" style={{ height: `${AudioBox.heightPercent}%` }} >
                         <div className="audiobox-playhead" style={{ width: AudioBox.playheadWidth }} title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div>
@@ -603,20 +569,20 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                             <div className="waveform">
                                 {this.waveform}
                             </div>
-                            {this.markerDocs.map((m, i) => {
-                                const isOverlap = this.isOverlap(m);
+                            {drawMarkers.map((d, i) => {
+                                const m = d.marker;
                                 const left = NumCast(m.audioStart) / this.audioDuration * timelineContentWidth;
-                                const top = isOverlap / (this.dataDoc.markerAmount + 1) * timelineContentHeight;
+                                const top = d.level / maxLevel * timelineContentHeight;
                                 const timespan = m.audioEnd === undefined ? 10 / timelineContentWidth * this.audioDuration : NumCast(m.audioEnd) - NumCast(m.audioStart);
                                 return this.layoutDoc.hideMarkers ? (null) :
                                     <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}timeline`} key={i}
-                                        style={{ left, top, width: `${timespan / this.audioDuration * 100}%`, height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%` }}
+                                        style={{ left, top, width: `${timespan / this.audioDuration * 100}%`, height: `${1 / maxLevel * 100}%` }}
                                         onClick={e => { this.playFrom(NumCast(m.audioStart), Cast(m.audioEnd, "number", null)); e.stopPropagation(); }} >
                                         {this.renderMarker(m, this.rangeClickScript, this.rangePlayScript,
                                             left + AudioBox.playheadWidth,
                                             (1 - AudioBox.heightPercent / 100) / 2 * this.props.PanelHeight() + top,
                                             timelineContentWidth * timespan / this.audioDuration,
-                                            timelineContentHeight / (this.dataDoc.markerAmount + 1))}
+                                            timelineContentHeight / maxLevel)}
                                     </div>;
                             })}
                             {this.selectionContainer}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 131e33e2a..6371ae5fb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -416,7 +416,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     } else func();
                 };
                 if (this.onDoubleClickHandler) {
-                    this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500);
+                    this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 350);
                 } else clickFunc();
             } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
                 this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right");
-- 
cgit v1.2.3-70-g09d2

