From 146f8622d5bac2edc6b09f57c173bd057dfbcfad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Jul 2022 00:17:26 -0400
Subject: restructured currentUserUtils to avoid having import cycles.

---
 .../collections/CollectionStackedTimeline.tsx      | 716 ++++++++-------------
 1 file changed, 279 insertions(+), 437 deletions(-)

(limited to 'src/client/views/collections/CollectionStackedTimeline.tsx')

diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b00017453..dcf3f7c51 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,50 +1,33 @@
-import React = require("react");
-import {
-    action,
-    computed,
-    IReactionDisposer,
-    observable,
-    reaction
-} from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import {
-    emptyFunction,
-    formatTime,
-    OmitKeys,
-    returnFalse,
-    returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent
-} from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { AudioWaveform } from "../AudioWaveform";
-import { CollectionSubView } from "../collections/CollectionSubView";
-import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import {
-    DocAfterFocusFunc,
-    DocFocusFunc,
-    DocumentView,
-    DocumentViewProps
-} from "../nodes/DocumentView";
-import { LabelBox } from "../nodes/LabelBox";
-import "./CollectionStackedTimeline.scss";
-import { VideoBox } from "../nodes/VideoBox";
-import { ImageField } from "../../../fields/URLField";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { AudioWaveform } from '../AudioWaveform';
+import { CollectionSubView } from '../collections/CollectionSubView';
+import { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { LabelBox } from '../nodes/LabelBox';
+import './CollectionStackedTimeline.scss';
+import { VideoBox } from '../nodes/VideoBox';
+import { ImageField } from '../../../fields/URLField';
 
 export type CollectionStackedTimelineProps = {
     Play: () => void;
@@ -68,7 +51,6 @@ export enum TrimScope {
     None = 0,
 }
 
-
 @observer
 export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
@@ -94,21 +76,38 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     @observable _thumbnail: string | undefined;
 
-    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore 
-    get minTrimLength() { return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5); }
-
-    @computed get trimStart() { return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart; }
-    @computed get trimDuration() { return this.trimEnd - this.trimStart; }
-    @computed get trimEnd() { return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd; }
+    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore
+    get minTrimLength() {
+        return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
+    }
 
-    @computed get clipStart() { return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart); }
-    @computed get clipDuration() { return this.clipEnd - this.clipStart; }
-    @computed get clipEnd() { return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration); }
+    @computed get trimStart() {
+        return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
+    }
+    @computed get trimDuration() {
+        return this.trimEnd - this.trimStart;
+    }
+    @computed get trimEnd() {
+        return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd;
+    }
 
-    @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
+    @computed get clipStart() {
+        return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart);
+    }
+    @computed get clipDuration() {
+        return this.clipEnd - this.clipStart;
+    }
+    @computed get clipEnd() {
+        return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration);
+    }
 
-    @computed get zoomFactor() { return this._zoomFactor; }
+    @computed get currentTime() {
+        return NumCast(this.layoutDoc._currentTimecode);
+    }
 
+    @computed get zoomFactor() {
+        return this._zoomFactor;
+    }
 
     constructor(props: any) {
         super(props);
@@ -117,32 +116,33 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             CollectionStackedTimeline.RangeScript ||
             ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
         CollectionStackedTimeline.RangePlayScript =
             CollectionStackedTimeline.RangePlayScript ||
             ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
     }
 
     componentDidMount() {
-        document.addEventListener("keydown", this.keyEvents, true);
+        document.addEventListener('keydown', this.keyEvents, true);
     }
 
     @action
     componentWillUnmount() {
-        document.removeEventListener("keydown", this.keyEvents, true);
+        document.removeEventListener('keydown', this.keyEvents, true);
         if (CollectionStackedTimeline.SelectingRegion === this) {
             CollectionStackedTimeline.SelectingRegion = undefined;
         }
     }
 
-
-    public get IsTrimming() { return this._trimming; }
+    public get IsTrimming() {
+        return this._trimming;
+    }
 
     @action
     public StartTrimming(scope: TrimScope) {
@@ -162,81 +162,63 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         this._zoomFactor = zoom;
     }
 
-
     anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
     anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val) ?? null);
 
-
     // converts screen pixel offset to time
     toTimeline = (screen_delta: number, width: number) => {
-        return Math.max(
-            this.clipStart,
-            Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
-    }
-
+        return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
+    };
 
     rangeClickScript = () => CollectionStackedTimeline.RangeScript;
     rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
 
-
     // handles key events for for creating key anchors, scrubbing, exiting trim
     @action
     keyEvents = (e: KeyboardEvent) => {
         if (
             // need to include range inputs because after dragging video time slider it becomes target element
-            !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+            !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
             this.props.isSelected(true)
         ) {
             // if shift pressed scrub 1 second otherwise 1/10th
             const jump = e.shiftKey ? 1 : 0.1;
             switch (e.key) {
-                case " ":
+                case ' ':
                     if (!CollectionStackedTimeline.SelectingRegion) {
                         this._markerStart = this._markerEnd = this.currentTime;
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
                     e.stopPropagation();
                     break;
-                case "Escape":
+                case 'Escape':
                     // abandons current trim
                     this._trimStart = this.clipStart;
                     this._trimStart = this.clipEnd;
                     this._trimming = TrimScope.None;
                     e.stopPropagation();
                     break;
-                case "ArrowLeft":
+                case 'ArrowLeft':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
                     e.stopPropagation();
                     break;
-                case "ArrowRight":
+                case 'ArrowRight':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
                     e.stopPropagation();
                     break;
             }
         }
-    }
-
+    };
 
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime = NumCast(
-            la2[this.props.startTag],
-            NumCast(la1[this.props.startTag])
-        );
+        const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -244,7 +226,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return { la1, la2, linkTime };
     }
 
-
     // handles dragging selection to create markers
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
@@ -259,7 +240,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             setupMoveUpEvents(
                 this,
                 e,
-                action((e) => {
+                action(e => {
                     if (!wasSelecting) {
                         this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
                         wasSelecting = true;
@@ -274,24 +255,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerStart = this._markerEnd;
                         this._markerEnd = tmp;
                     }
-                    if (
-                        !isClick &&
-                        Math.abs(movement[0]) > 15 &&
-                        !this.IsTrimming
-                    ) {
-                        const anchor = CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                    if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
+                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
-                    (!isClick || !wasSelecting) &&
-                        (this._markerEnd = undefined);
+                    (!isClick || !wasSelecting) && (this._markerEnd = undefined);
                 }),
                 (e, doubleTap) => {
                     if (e.button !== 2) {
@@ -303,23 +271,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this.currentTime
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
                 }
             );
         }
-
-    }
-
+    };
 
     @action
     onHover = (e: React.MouseEvent): void => {
@@ -329,15 +288,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         if (rect) {
             this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
             if (this.dataDoc.thumbnails) {
-                const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails);
-                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []);
-                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField("");
-                const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : "";
+                const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
+                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec('string'), []);
+                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField('');
+                const src = imgField && imgField.url.href ? imgField.url.href.replace('.png', '_s.png') : '';
                 this._thumbnail = src ? src : undefined;
             }
         }
-    }
-
+    };
 
     // for dragging trim start handle
     @action
@@ -348,13 +306,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimStart = Math.min(
-                        Math.max(
-                            this.trimStart + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipStart
-                        ),
-                        this.trimEnd - this.minTrimLength
-                    );
+                    this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
                 }
                 return false;
             }),
@@ -363,7 +315,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimStart = this.clipStart);
             })
         );
-    }
+    };
 
     // for dragging trim end handle
     @action
@@ -374,13 +326,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimEnd = Math.max(
-                        Math.min(
-                            this.trimEnd + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipEnd
-                        ),
-                        this.trimStart + this.minTrimLength
-                    );
+                    this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
                 }
                 return false;
             }),
@@ -389,15 +335,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimEnd = this.clipEnd);
             })
         );
-    }
-
+    };
 
     // for rendering scrolling when timeline zoomed
     @action
     setScroll = (e: React.UIEvent) => {
         e.stopPropagation();
         this._scroll = this._timelineWrapper!.scrollLeft;
-    }
+    };
 
     // smooth scrolls to time like when following links overflowed due to zoom
     @action
@@ -406,14 +351,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             if (time > this.toTimeline(this._scroll + this.props.PanelWidth(), this.timelineContentWidth)) {
                 this._scroll = Math.min(this._scroll + this.props.PanelWidth(), this.timelineContentWidth - this.props.PanelWidth());
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
-            }
-            else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
-                this._scroll = time / this.timelineContentWidth * this.clipDuration;
+            } else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
+                this._scroll = (time / this.timelineContentWidth) * this.clipDuration;
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
             }
         }
-    }
-
+    };
 
     // handles dragging and dropping markers in timeline
     @action
@@ -428,9 +371,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         docDragData.droppedDocuments.forEach(drop => {
             const anchorEnd = this.anchorEnd(drop);
             if (anchorEnd !== undefined) {
-                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : "timecodeToHide", timelinePt + anchorEnd - this.anchorStart(drop), false);
+                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
             }
-            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : "timecodeToShow", timelinePt, false);
+            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
         });
 
         return true;
@@ -439,38 +382,28 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
         return false;
-    }
-
+    };
 
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(
-        rootDoc: Doc,
-        dataDoc: Doc,
-        fieldKey: string,
-        startTag: string,
-        endTag: string,
-        anchorStartTime?: number,
-        anchorEndTime?: number,
-        docAnchor?: Doc
-    ) {
+    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
         if (anchorStartTime === undefined) return rootDoc;
-        const anchor = docAnchor ?? Docs.Create.LabelDocument({
-            title: ComputedField.MakeFunction(
-                `self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`
-            ) as any,
-            _minFontSize: 12,
-            _maxFontSize: 24,
-            _singleLine: false,
-            _stayInCollection: true,
-            useLinkSmallAnchor: true,
-            hideLinkButton: true,
-            _isLinkButton: true,
-            annotationOn: rootDoc,
-            _timelineLabel: true,
-            borderRounding: anchorEndTime === undefined ? "100%" : undefined
-        });
+        const anchor =
+            docAnchor ??
+            Docs.Create.LabelDocument({
+                title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
+                _minFontSize: 12,
+                _maxFontSize: 24,
+                _singleLine: false,
+                _stayInCollection: true,
+                useLinkSmallAnchor: true,
+                hideLinkButton: true,
+                _isLinkButton: true,
+                annotationOn: rootDoc,
+                _timelineLabel: true,
+                borderRounding: anchorEndTime === undefined ? '100%' : undefined,
+            });
         Doc.GetProto(anchor)[startTag] = anchorStartTime;
         Doc.GetProto(anchor)[endTag] = anchorEndTime;
         if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
@@ -481,7 +414,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return anchor;
     }
 
-
     @action
     playOnClick = (anchorDoc: Doc, clientX: number) => {
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
@@ -493,10 +425,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 this.scrollToTime(seekTimeInSeconds);
             }
         } else {
-            if (
-                seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) &&
-                endTime > NumCast(this.layoutDoc._currentTimecode)
-            ) {
+            if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
                 if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
                     this.props.Pause();
                 } else {
@@ -508,59 +437,43 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             }
         }
         return { select: true };
-    }
+    };
 
     @action
     clickAnchor = (anchorDoc: Doc, clientX: number) => {
         if (anchorDoc.isLinkButton) {
-            LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+            LinkFollower.FollowLink(undefined, anchorDoc, this.props, false);
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
         const endTime = this.anchorEnd(anchorDoc);
-        if (
-            seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 &&
-            endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4
-        ) {
+        if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
             if (this.props.playing()) this.props.Pause();
             else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
             else if (!this.layoutDoc.autoPlayAnchors) {
                 const rect = this._timeline?.getBoundingClientRect();
-                rect &&
-                    this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+                rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
             }
         } else {
             if (this.layoutDoc.autoPlayAnchors) {
                 this.props.playFrom(seekTimeInSeconds, endTime);
-            }
-            else {
+            } else {
                 this.props.setTime(seekTimeInSeconds);
             }
         }
         return { select: true };
-    }
+    };
 
     // makes sure no anchors overlaps each other by setting the correct position and width
-    getLevel = (
-        m: Doc,
-        placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]
-    ) => {
+    getLevel = (m: Doc, placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]) => {
         const timelineContentWidth = this.timelineContentWidth;
         const x1 = this.anchorStart(m);
-        const x2 = this.anchorEnd(
-            m,
-            x1 + (10 / timelineContentWidth) * this.clipDuration
-        );
+        const x2 = this.anchorEnd(m, x1 + (10 / timelineContentWidth) * this.clipDuration);
         let max = 0;
         const overlappedLevels = new Set(
-            placed.map((p) => {
+            placed.map(p => {
                 const y1 = p.anchorStartTime;
                 const y2 = p.anchorEndTime;
-                if (
-                    (x1 >= y1 && x1 <= y2) ||
-                    (x2 >= y1 && x2 <= y2) ||
-                    (y1 >= x1 && y1 <= x2) ||
-                    (y2 >= x1 && y2 <= x2)
-                ) {
+                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;
                 }
@@ -571,14 +484,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
         placed.push({ anchorStartTime: x1, anchorEndTime: x2, level });
         return level;
-    }
-
+    };
 
     dictationHeightPercent = 50;
     dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
 
-    @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; }
-    @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; }  // subtract size of container border
+    @computed get timelineContentHeight() {
+        return (this.props.PanelHeight() * this.dictationHeightPercent) / 100;
+    }
+    @computed get timelineContentWidth() {
+        return this.props.PanelWidth() * this.zoomFactor;
+    } // subtract size of container border
 
     dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight);
 
@@ -586,24 +502,18 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     currentTimecode = () => this.currentTime;
 
-
     @computed get renderDictation() {
         const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
         return !dictation ? null : (
             <div
                 style={{
-                    position: "absolute",
-                    height: "100%",
+                    position: 'absolute',
+                    height: '100%',
                     top: this.timelineContentHeight,
                     background: Colors.LIGHT_BLUE,
-                }}
-            >
+                }}>
                 <DocumentView
-                    {...OmitKeys(this.props, [
-                        "NativeWidth",
-                        "NativeHeight",
-                        "setContentView",
-                    ]).omit}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                     Document={dictation}
                     PanelHeight={this.dictationHeight}
                     isAnnotationOverlay={true}
@@ -618,8 +528,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                     moveDocument={returnFalse}
                     addDocument={returnFalse}
                     CollectionView={undefined}
-                    renderDepth={this.props.renderDepth + 1}
-                ></DocumentView>
+                    renderDepth={this.props.renderDepth + 1}></DocumentView>
             </div>
         );
     }
@@ -644,145 +553,131 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             anchorEndTime: number;
             level: number;
         }[] = [];
-        const drawAnchors = this.childDocs.map((anchor) => ({
+        const drawAnchors = this.childDocs.map(anchor => ({
             level: this.getLevel(anchor, overlaps),
             anchor,
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
-        return (<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined }}>
-            <div className="timeline-container"
-                style={{ width: this.props.PanelWidth() }}
-                onWheel={e => e.stopPropagation()}
-                onScroll={this.setScroll}
-                onMouseMove={(e) => this.isContentActive() && this.onHover(e)}
-                ref={wrapper => this._timelineWrapper = wrapper}>
+        return (
+            <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
                 <div
-                    className="collectionStackedTimeline"
-                    ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
-                    onClick={(e) => this.isContentActive() && StopEvent(e)}
-                    onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)}
-                    style={{ width: this.timelineContentWidth }}>
-
-                    {drawAnchors.map((d) => {
-                        const start = this.anchorStart(d.anchor);
-                        const end = this.anchorEnd(
-                            d.anchor,
-                            start + (10 / this.timelineContentWidth) * this.clipDuration
-                        );
-                        if (end < this.clipStart || start > this.clipEnd) return (null);
-                        const left = Math.max((start - this.clipStart) / this.clipDuration * this.timelineContentWidth, 0);
-                        const top = (d.level / maxLevel) * this.props.PanelHeight();
-                        const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
-                        const width = (timespan / this.clipDuration) * this.timelineContentWidth;
-                        const height = this.props.PanelHeight() / maxLevel;
-                        return this.props.Document.hideAnchors ? null : (
-                            <div
-                                className={"collectionStackedTimeline-marker-timeline"}
-                                key={d.anchor[Id]}
-                                style={{
-                                    left,
-                                    top,
-                                    width: `${width}px`,
-                                    height: `${height}px`,
-                                }}
-                                onClick={(e) => {
-                                    this.props.playFrom(start, this.anchorEnd(d.anchor));
-                                    e.stopPropagation();
-                                }}
-                            >
-                                <StackedTimelineAnchor
-                                    {...this.props}
-                                    mark={d.anchor}
-                                    rangeClickScript={this.rangeClickScript}
-                                    rangePlayScript={this.rangePlayScript}
-                                    left={left - this._scroll}
-                                    top={top}
-                                    width={width}
-                                    height={height}
-                                    toTimeline={this.toTimeline}
-                                    layoutDoc={this.layoutDoc}
-                                    // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                                    currentTimecode={this.currentTimecode}
-                                    _timeline={this._timeline}
-                                    stackedTimeline={this}
-                                    trimStart={this.trimStart}
-                                    trimEnd={this.trimEnd}
-                                />
-                            </div>
-                        );
-                    })}
-                    {!this.IsTrimming && this.selectionContainer}
-                    <AudioWaveform
-                        rawDuration={this.props.rawDuration}
-                        duration={this.clipDuration}
-                        mediaPath={this.props.mediaPath}
-                        layoutDoc={this.layoutDoc}
-                        clipStart={this.clipStart}
-                        clipEnd={this.clipEnd}
-                        zoomFactor={this.zoomFactor}
-                        PanelHeight={this.timelineContentHeight}
-                        PanelWidth={this.timelineContentWidth}
-                    />
-                    {/* {this.renderDictation} */}
-
+                    className="timeline-container"
+                    style={{ width: this.props.PanelWidth() }}
+                    onWheel={e => e.stopPropagation()}
+                    onScroll={this.setScroll}
+                    onMouseMove={e => this.isContentActive() && this.onHover(e)}
+                    ref={wrapper => (this._timelineWrapper = wrapper)}>
                     <div
-                        className="collectionStackedTimeline-hover"
-                        style={{
-                            left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
+                        className="collectionStackedTimeline"
+                        ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+                        onClick={e => this.isContentActive() && StopEvent(e)}
+                        onPointerDown={e => this.isContentActive() && this.onPointerDownTimeline(e)}
+                        style={{ width: this.timelineContentWidth }}>
+                        {drawAnchors.map(d => {
+                            const start = this.anchorStart(d.anchor);
+                            const end = this.anchorEnd(d.anchor, start + (10 / this.timelineContentWidth) * this.clipDuration);
+                            if (end < this.clipStart || start > this.clipEnd) return null;
+                            const left = Math.max(((start - this.clipStart) / this.clipDuration) * this.timelineContentWidth, 0);
+                            const top = (d.level / maxLevel) * this.props.PanelHeight();
+                            const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
+                            const width = (timespan / this.clipDuration) * this.timelineContentWidth;
+                            const height = this.props.PanelHeight() / maxLevel;
+                            return this.props.Document.hideAnchors ? null : (
+                                <div
+                                    className={'collectionStackedTimeline-marker-timeline'}
+                                    key={d.anchor[Id]}
+                                    style={{
+                                        left,
+                                        top,
+                                        width: `${width}px`,
+                                        height: `${height}px`,
+                                    }}
+                                    onClick={e => {
+                                        this.props.playFrom(start, this.anchorEnd(d.anchor));
+                                        e.stopPropagation();
+                                    }}>
+                                    <StackedTimelineAnchor
+                                        {...this.props}
+                                        mark={d.anchor}
+                                        rangeClickScript={this.rangeClickScript}
+                                        rangePlayScript={this.rangePlayScript}
+                                        left={left - this._scroll}
+                                        top={top}
+                                        width={width}
+                                        height={height}
+                                        toTimeline={this.toTimeline}
+                                        layoutDoc={this.layoutDoc}
+                                        // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                                        currentTimecode={this.currentTimecode}
+                                        _timeline={this._timeline}
+                                        stackedTimeline={this}
+                                        trimStart={this.trimStart}
+                                        trimEnd={this.trimEnd}
+                                    />
+                                </div>
+                            );
+                        })}
+                        {!this.IsTrimming && this.selectionContainer}
+                        <AudioWaveform
+                            rawDuration={this.props.rawDuration}
+                            duration={this.clipDuration}
+                            mediaPath={this.props.mediaPath}
+                            layoutDoc={this.layoutDoc}
+                            clipStart={this.clipStart}
+                            clipEnd={this.clipEnd}
+                            zoomFactor={this.zoomFactor}
+                            PanelHeight={this.timelineContentHeight}
+                            PanelWidth={this.timelineContentWidth}
+                        />
+                        {/* {this.renderDictation} */}
+
+                        <div
+                            className="collectionStackedTimeline-hover"
+                            style={{
+                                left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        <div
+                            className="collectionStackedTimeline-current"
+                            style={{
+                                left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        {this.IsTrimming !== TrimScope.None && (
+                            <>
+                                <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}></div>
 
-                    <div
-                        className="collectionStackedTimeline-current"
-                        style={{
-                            left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
-
-                    {this.IsTrimming !== TrimScope.None && (
-                        <>
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}
-                            ></div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-controls"
-                                style={{
-                                    left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
-                                }}
-                            >
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimLeft}
-                                ></div>
+                                    className="collectionStackedTimeline-trim-controls"
+                                    style={{
+                                        left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
+                                    }}>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft}></div>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight}></div>
+                                </div>
+
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimRight}
-                                ></div>
-                            </div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{
-                                    left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
-                                }}
-                            ></div>
-                        </>
-                    )}
+                                    className="collectionStackedTimeline-trim-shade"
+                                    style={{
+                                        left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
+                                    }}></div>
+                            </>
+                        )}
+                    </div>
+                </div>
+                <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
+                    <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
+                    {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
                 </div>
             </div>
-            <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
-                <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
-                {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
-            </div>
-        </div >);
+        );
     }
 }
 
-
 /**
  * StackedTimelineAnchor
  * creates the anchors to display markers, links, and embedded documents on timeline
@@ -814,7 +709,6 @@ interface StackedTimelineAnchorProps {
     trimEnd: number;
 }
 
-
 @observer
 class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
     _lastTimecode: number;
@@ -831,23 +725,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
         const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
         return `#${formatTime(start)}-${formatTime(end)}`;
-    }
+    };
 
     componentDidMount() {
         this._disposer = reaction(
             () => this.props.currentTimecode(),
-            (time) => {
-                const dictationDoc = Cast(
-                    this.props.layoutDoc["data-dictation"],
-                    Doc,
-                    null
-                );
-                const isDictation =
-                    dictationDoc &&
-                    DocListCast(this.props.mark.links).some(
-                        (link) =>
-                            Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc
-                    );
+            time => {
+                const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
+                const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
                 if (
                     !LightboxView.LightboxDoc &&
                     // bcz: when should links be followed?  we don't want to move away from the video to follow a link but we can open it in a sidebar/etc.  But we don't know that upfront.
@@ -859,13 +744,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     time < NumCast(this.props.mark[this.props.endTag]) &&
                     this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
                 ) {
-                    LinkManager.FollowLink(
-                        undefined,
-                        this.props.mark,
-                        this.props as any as DocumentViewProps,
-                        false,
-                        true
-                    );
+                    LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
                 }
                 this._lastTimecode = time;
             }
@@ -876,7 +755,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         this._disposer?.();
     }
 
-
     // starting the drag event for anchor resizing
     onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
         this.props._timeline?.setPointerCapture(e.pointerId);
@@ -885,19 +763,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             return this.props.toTimeline(e.clientX - rect.x, rect.width);
         };
         const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => {
-            const timelineOnly = Cast(anchor[this.props.startTag], "number", null) !== undefined;
+            const timelineOnly = Cast(anchor[this.props.startTag], 'number', null) !== undefined;
             if (timelineOnly) {
                 if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined;
-                Doc.SetInPlace(
-                    anchor,
-                    left ? this.props.startTag : this.props.endTag,
-                    time,
-                    true
-                );
-                if (!left) Doc.SetInPlace(anchor, "borderRounding", time !== undefined ? undefined : "100%", true);
-            }
-            else {
-                anchor[left ? "_timecodeToShow" : "_timecodeToHide"] = time;
+                Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
+                if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true);
+            } else {
+                anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
             }
             return false;
         };
@@ -906,46 +778,34 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         setupMoveUpEvents(
             this,
             e,
-            (e) => {
-                if (!undo) undo = UndoManager.StartBatch("drag anchor");
+            e => {
+                if (!undo) undo = UndoManager.StartBatch('drag anchor');
                 this.props.setTime(newTime(e));
                 return changeAnchor(anchor, left, newTime(e));
             },
-            (e) => {
+            e => {
                 this.props.setTime(newTime(e));
                 this.props._timeline?.releasePointerCapture(e.pointerId);
                 undo?.end();
             },
             emptyFunction
         );
-    }
-
+    };
 
     // context menu
     contextMenuItems = () => {
-        const resetTitle = { script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!, icon: "folder-plus", label: "Reset Title" };
+        const resetTitle = {
+            script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!,
+            icon: 'folder-plus',
+            label: 'Reset Title',
+        };
         return [resetTitle];
-    }
-
+    };
 
     // renders anchor LabelBox
-    renderInner = computedFn(function (
-        this: StackedTimelineAnchor,
-        mark: Doc,
-        script: undefined | (() => ScriptField),
-        doublescript: undefined | (() => ScriptField),
-        screenXf: () => Transform,
-        width: () => number,
-        height: () => number
-    ) {
+    renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as any });
-        const focusFunc = (
-            doc: Doc,
-            willZoom?: boolean,
-            scale?: number,
-            afterFocus?: DocAfterFocusFunc,
-            docTransform?: Transform
-        ) => {
+        const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
             this.props.playLink(mark);
             this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
         };
@@ -954,13 +814,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             view: (
                 <DocumentView
                     key="view"
-                    {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                    ref={action((r: DocumentView | null) => anchor.view = r)}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                    ref={action((r: DocumentView | null) => (anchor.view = r))}
                     Document={mark}
                     DataDoc={undefined}
                     renderDepth={this.props.renderDepth + 1}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LabelBox.LayoutStringWithTitle("data", this.computeTitle())}
+                    LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
@@ -985,32 +845,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     height = () => this.props.height;
 
     render() {
-        const inner = this.renderInner(
-            this.props.mark,
-            this.props.rangeClickScript,
-            this.props.rangePlayScript,
-            this.anchorScreenToLocalXf,
-            this.width,
-            this.height
-        );
+        const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height);
         return (
             <>
                 {inner.view}
-                {!inner.anchor.view ||
-                    !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+                {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
                     <>
-                        <div
-                            key="left"
-                            className="collectionStackedTimeline-left-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
-                        />
-                        <div
-                            key="right"
-                            className="collectionStackedTimeline-resizer"
-                            onPointerDown={(e) =>
-                                this.onAnchorDown(e, this.props.mark, false)
-                            }
-                        />
+                        <div key="left" className="collectionStackedTimeline-left-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
+                        <div key="right" className="collectionStackedTimeline-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
                     </>
                 )}
             </>
@@ -1025,4 +867,4 @@ ScriptingGlobals.add(function min(num1: number, num2: number): number {
 });
 ScriptingGlobals.add(function max(num1: number, num2: number): number {
     return Math.max(num1, num2);
-});
\ No newline at end of file
+});
-- 
cgit v1.2.3-70-g09d2

