From 4574b7f03ccc85c4bebdbfd9475788456086704f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 8 Aug 2024 12:27:40 -0400
Subject: many changes to add typing in place of 'any's etc

---
 .../views/collections/CollectionTreeView.tsx       | 43 +++++++++++-----------
 1 file changed, 21 insertions(+), 22 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 285598600..ff316cfbd 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,8 +1,7 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import ResizeObserver from 'resize-observer-polyfill';
 import { DivHeight, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
 import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
@@ -19,7 +18,7 @@ import { dropActionType } from '../../util/DropActionTypes';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
@@ -27,13 +26,11 @@ import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProp';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView } from './CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import './CollectionTreeView.scss';
 import { TreeViewType } from './CollectionTreeViewType';
 import { TreeView } from './TreeView';
 
-const _global = (window /* browser */ || global) /* node */ as any;
-
 export type collectionTreeViewProps = {
     treeViewExpandedView?: 'fields' | 'layout' | 'links' | 'data';
     treeViewOpen?: boolean;
@@ -55,10 +52,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     private _titleRef?: HTMLDivElement | HTMLInputElement | null;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _isDisposing = false; // notes that instance is in process of being disposed
-    private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes
-    private observer: any; // observer for monitoring tree view items.
+    private refList: Set<HTMLElement> = new Set(); // list of tree view items to monitor for height changes
+    private observer: ResizeObserver | undefined; // observer for monitoring tree view items.
 
-    constructor(props: any) {
+    constructor(props: SubCollectionViewProps & collectionTreeViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -113,14 +110,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             !this._props.dontRegisterView && this._props.setHeight?.(bodyHeight + titleHeight);
         }
     };
-    unobserveHeight = (ref: any) => {
+    unobserveHeight = (ref: HTMLElement) => {
         this.refList.delete(ref);
         this.layoutDoc.layout_autoHeight && this.computeHeight();
     };
-    observeHeight = (ref: any) => {
+    observeHeight = (ref: HTMLElement) => {
         if (ref) {
             this.refList.add(ref);
-            this.observer = new _global.ResizeObserver(() => {
+            this.observer = new ResizeObserver(() => {
                 if (this.layoutDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.IsDragging) {
                     this.computeHeight();
                 }
@@ -216,7 +213,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
         if (!Doc.noviceMode) {
             const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
-            const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+            const onClicks: ContextMenuProps[] = existingOnClick.subitems ?? [];
             onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
             !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
         }
@@ -234,11 +231,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 maxHeight={72}
                 height="auto"
                 GetValue={() => StrCast(this.dataDoc.title)}
-                SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                SetValue={undoable((value: string, shift: boolean, enter: boolean) => {
                     if (enter && this.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
                     this.dataDoc.title = value;
                     return true;
-                })}
+                }, 'set doc title')}
             />
         );
     }
@@ -285,7 +282,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
-        const dragAction = StrCast(this.Document.childDragAction) as any as dropActionType;
+        const dragAction = StrCast(this.Document.childDragAction) as dropActionType;
         const treeAddDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this._props.moveDocument?.(d, target, addDoc) || false;
         if (this._renderCount < this.treeChildren.length)
@@ -333,9 +330,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.dataDoc === null ? null : (
             <div
                 className="collectionTreeView-titleBar"
-                ref={action((r: any) => {
-                    (this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.ScreenToLocalBoxXf().Scale);
-                })}
+                ref={r =>
+                    runInAction(() => {
+                        (this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.ScreenToLocalBoxXf().Scale);
+                    })
+                }
                 key={this.Document[Id]}
                 style={!this.outlineMode ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}}>
                 {this.outlineMode ? this.documentTitle : this.editableTitle}
@@ -410,8 +409,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
 
     @observable _headerHeight = 0;
     @computed get content() {
-        const background = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor);
-        const color = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.Color);
+        const background = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string;
+        const color = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.Color) as string;
         const pointerEvents = () => (this._props.isContentActive() === false ? 'none' : undefined);
         const titleBar = this._props.treeViewHideTitle || this.Document.treeView_HideTitle ? null : this.titleBar;
         return (
-- 
cgit v1.2.3-70-g09d2


From 762ac2bf354e4cc2c4b15f42502da939f5061646 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 9 Aug 2024 16:48:18 -0400
Subject: a bunch more typing fixes.

---
 src/client/util/BranchingTrailManager.tsx          |  14 +-
 src/client/util/reportManager/ReportManager.scss   |  12 +-
 src/client/views/ContextMenu.scss                  |  37 ++---
 src/client/views/ContextMenu.tsx                   |  32 ++--
 src/client/views/ContextMenuItem.tsx               | 180 ++++++---------------
 src/client/views/DictationOverlay.tsx              |   2 +-
 src/client/views/DocViewUtils.ts                   |   1 +
 src/client/views/DocumentButtonBar.tsx             |  11 +-
 src/client/views/DocumentDecorations.tsx           |  23 +--
 src/client/views/EditableView.tsx                  |  26 +--
 src/client/views/ExtractColors.ts                  |   2 +-
 src/client/views/FieldsDropdown.tsx                |   8 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/KeyphraseQueryView.scss           |   8 -
 src/client/views/KeyphraseQueryView.tsx            |  32 ----
 src/client/views/MainViewModal.tsx                 |   2 -
 src/client/views/ObservableReactComponent.tsx      |  14 +-
 src/client/views/PreviewCursor.tsx                 |  51 +++---
 src/client/views/PropertiesButtons.tsx             |  19 +--
 .../views/PropertiesDocBacklinksSelector.tsx       |   2 +-
 src/client/views/PropertiesDocContextSelector.tsx  |   7 +-
 src/client/views/PropertiesSection.tsx             |   4 +-
 src/client/views/PropertiesView.tsx                | 102 ++++++------
 src/client/views/SidebarAnnos.tsx                  |  10 +-
 src/client/views/StyleProvider.tsx                 |  11 +-
 src/client/views/TemplateMenu.tsx                  |   3 +-
 src/client/views/UndoStack.tsx                     |   2 -
 .../views/collections/CollectionCarouselView.tsx   |   3 +-
 src/client/views/collections/CollectionMenu.tsx    |  44 ++---
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/CollectionView.tsx    |  12 +-
 src/client/views/global/globalScripts.ts           |  32 ++--
 src/client/views/linking/LinkMenu.tsx              |   4 +-
 src/client/views/linking/LinkMenuGroup.tsx         |   2 -
 src/client/views/linking/LinkMenuItem.tsx          |  20 ++-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   4 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |  20 +--
 src/client/views/nodes/KeyValueBox.tsx             |   3 +-
 src/client/views/nodes/KeyValuePair.tsx            |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   6 +-
 .../views/nodes/formattedText/EquationView.tsx     |  10 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   6 +-
 43 files changed, 333 insertions(+), 460 deletions(-)
 delete mode 100644 src/client/views/KeyphraseQueryView.scss
 delete mode 100644 src/client/views/KeyphraseQueryView.tsx

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

diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx
index 119d103c5..65336812d 100644
--- a/src/client/util/BranchingTrailManager.tsx
+++ b/src/client/util/BranchingTrailManager.tsx
@@ -15,18 +15,18 @@ export class BranchingTrailManager extends React.Component {
     public static Instance: BranchingTrailManager;
 
     // stack of the history
-    @observable private slideHistoryStack: String[] = [];
-    @observable private containsSet: Set<String> = new Set<String>();
+    @observable private slideHistoryStack: string[] = [];
+    @observable private containsSet: Set<string> = new Set<string>();
     // docId to Doc map
-    @observable private docIdToDocMap: Map<String, Doc> = new Map<String, Doc>();
+    @observable private docIdToDocMap: Map<string, Doc> = new Map<string, Doc>();
 
     // prev pres to copmare with
-    @observable private prevPresId: String | null = null;
-    @action setPrevPres = action((newId: String | null) => {
+    @observable private prevPresId: string | null = null;
+    @action setPrevPres = action((newId: string | null) => {
         this.prevPresId = newId;
     });
 
-    constructor(props: any) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         if (!BranchingTrailManager.Instance) {
@@ -48,7 +48,7 @@ export class BranchingTrailManager extends React.Component {
         // Doc.AddToMyOverlay(hi);
     };
 
-    @action setSlideHistoryStack = action((newArr: String[]) => {
+    @action setSlideHistoryStack = action((newArr: string[]) => {
         this.slideHistoryStack = newArr;
     });
 
diff --git a/src/client/util/reportManager/ReportManager.scss b/src/client/util/reportManager/ReportManager.scss
index d82d7fdeb..fd343ac8e 100644
--- a/src/client/util/reportManager/ReportManager.scss
+++ b/src/client/util/reportManager/ReportManager.scss
@@ -96,12 +96,12 @@
     transition: all 0.2s ease;
     background: transparent;
 
-    &:hover {
-        // border-bottom-color: $text-gray;
-    }
-    &:focus {
-        // border-bottom-color: #4476f7;
-    }
+    // &:hover {
+    //     // border-bottom-color: $text-gray;
+    // }
+    // &:focus {
+    //     // border-bottom-color: #4476f7;
+    // }
 }
 
 // View issues
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 232362c5c..4aaf2d03b 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -38,7 +38,12 @@
     background: whitesmoke;
 }
 
-.contextMenu-item {
+.contextMenuItem-Selected {
+    background: lightgoldenrodyellow;
+    border-style: none;
+}
+
+.contextMenuItem {
     // width: 11vw; //10vw
     height: 25px; //2vh
     display: flex; //comment out to allow search icon to be inline with search text
@@ -59,7 +64,7 @@
     text-transform: uppercase;
     padding-right: 30px;
 
-    .contextMenu-item-background {
+    .contextMenuItem-background {
         width: 100%;
         height: 100%;
         position: absolute;
@@ -69,13 +74,7 @@
         filter: opacity(0);
     }
 
-    &:hover {
-        .contextMenu-item-background {
-            filter: opacity(0.2) !important;
-        }
-    }
-
-    .contextMenu-item-icon-background {
+    .contextMenuItem-icon {
         pointer-events: all;
         background-color: transparent;
         width: 35px;
@@ -103,6 +102,8 @@
     letter-spacing: 1px;
     text-transform: uppercase;
     padding-right: 30px;
+    align-items: center;
+    align-self: center;
 }
 
 .contextMenu-item:hover {
@@ -115,11 +116,6 @@
     cursor: pointer;
 }
 
-.contextMenu-itemSelected {
-    background: lightgoldenrodyellow;
-    border-style: none;
-}
-
 .contextMenu-group {
     // width: 11vw; //10vw
     height: 30px; //2vh
@@ -145,23 +141,24 @@
     padding-left: 5px;
 }
 
-.contextMenu-inlineMenu {
-    // border-top: solid 1px; //TODO:glr clean
-}
-
 .contextMenu-description {
     margin-left: 5px;
     text-align: left;
     display: inline; //need this?
 }
 
-.search-icon {
+.contextMenu-search {
     margin: 10px;
+    display: flex;
+    .contextMenu-searchIcon {
+        margin-right: 5px;
+    }
 }
 
-.search {
+.contextMenu-searchInput {
     margin-left: 10px;
     padding-left: 10px;
     border: solid black 1px;
     border-radius: 5px;
+    width: 100%;
 }
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 05634f376..5edb5fc0d 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -8,7 +8,7 @@ import * as React from 'react';
 import { DivHeight, DivWidth } from '../../ClientUtils';
 import { SnappingManager } from '../util/SnappingManager';
 import './ContextMenu.scss';
-import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem';
+import { ContextMenuItem, ContextMenuProps } from './ContextMenuItem';
 import { ObservableReactComponent } from './ObservableReactComponent';
 
 @observer
@@ -148,13 +148,13 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }
         return wasOpen;
     };
 
-    @computed get filteredItems(): (OriginalMenuProps | string[])[] {
+    @computed get filteredItems(): (ContextMenuProps | string[])[] {
         const searchString = this._searchString.toLowerCase().split(' ');
         const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s)));
         const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: string) => string[]) => {
-            let eles: (OriginalMenuProps | string[])[] = [];
+            let eles: (ContextMenuProps | string[])[] = [];
 
-            const leaves: OriginalMenuProps[] = [];
+            const leaves: ContextMenuProps[] = [];
             items.forEach(item => {
                 const { description } = item;
                 const path = groupFunc(description);
@@ -165,7 +165,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }
                         eles = eles.concat(children);
                     }
                 } else if (matches(path)) {
-                    leaves.push(item as OriginalMenuProps);
+                    leaves.push(item as ContextMenuProps);
                 }
             });
 
@@ -176,8 +176,8 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }
         return flattenItems(this._items.slice(), name => [name]);
     }
 
-    @computed get flatItems(): OriginalMenuProps[] {
-        return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[];
+    @computed get flatItems(): ContextMenuProps[] {
+        return this.filteredItems.filter(item => !Array.isArray(item)) as ContextMenuProps[];
     }
 
     @computed get menuItems() {
@@ -228,21 +228,11 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }
                     color: SnappingManager.userColor,
                 }}>
                 {!this.itemsNeedSearch ? null : (
-                    <span className="search-icon">
-                        <span className="icon-background">
+                    <span className="contextMenu-search">
+                        <span className="contextMenu-searchIcon">
                             <FontAwesomeIcon icon="search" size="lg" />
                         </span>
-                        <input
-                            ref={this._searchRef}
-                            style={{ color: 'black' }}
-                            className="contextMenu-item contextMenu-description search"
-                            type="text"
-                            placeholder="Filter Menu..."
-                            value={this._searchString}
-                            onKeyDown={this.onKeyDown}
-                            onChange={this.onChange}
-                            autoFocus
-                        />
+                        <input ref={this._searchRef} style={{ color: 'black' }} className="contextMenu-searchInput" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
                     </span>
                 )}
                 {this.menuItems}
@@ -264,7 +254,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }
             e.preventDefault();
         } else if (e.key === 'Enter' || e.key === 'Tab') {
             const item = this.flatItems[this._selectedIndex];
-            if (item) {
+            if (item.event) {
                 item.event({ x: this.pageX, y: this.pageY });
             } else {
                 // if (this._searchString.startsWith(this._defaultPrefix)) {
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 3b87ea58b..5b4eb704b 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -8,32 +8,27 @@ import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
 import { ObservableReactComponent } from './ObservableReactComponent';
 
-export interface OriginalMenuProps {
+export interface ContextMenuProps {
+    icon: IconProp | JSX.Element;
     description: string;
-    event: (stuff?: unknown) => void;
-    undoable?: boolean;
-    noexpand?: boolean;
-    subitems?: ContextMenuProps[];
-    icon: IconProp | JSX.Element; // maybe should be optional (icon?)
-    closeMenu?: () => void;
-}
-
-export interface SubmenuProps {
-    description: string;
-    // eslint-disable-next-line no-use-before-define
-    subitems: ContextMenuProps[];
-    noexpand?: boolean;
     addDivider?: boolean;
-    icon: IconProp; // maybe should be optional (icon?)
     closeMenu?: () => void;
-}
 
-export type ContextMenuProps = OriginalMenuProps | SubmenuProps;
+    subitems?: ContextMenuProps[];
+    noexpand?: boolean; // whether to render the submenu items as a flyout from this item, or inline in place of this item
+
+    undoable?: boolean; // whether to wrap the event callback in an UndoBatch or not
+    event?: (stuff?: unknown) => void;
+}
 
 @observer
 export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps & { selected?: boolean }> {
-    @observable private _items: Array<ContextMenuProps> = [];
-    @observable private overItem = false;
+    static readonly HOVER_TIMEOUT = 100;
+    _hoverTimeout?: NodeJS.Timeout;
+    _overPosY = 0;
+    _overPosX = 0;
+    @observable _items: ContextMenuProps[] = [];
+    @observable _overItem = false;
 
     constructor(props: ContextMenuProps & { selected?: boolean }) {
         super(props);
@@ -41,132 +36,63 @@ export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps &
     }
 
     componentDidMount() {
-        runInAction(() => {
-            this._items.length = 0;
-        });
-        if ((this._props as SubmenuProps)?.subitems) {
-            (this._props as SubmenuProps).subitems?.forEach(i => runInAction(() => this._items.push(i)));
-        }
+        runInAction(() => this._items.push(...(this._props.subitems ?? [])));
     }
 
     handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => {
-        if ('event' in this._props) {
+        if (this._props.event) {
             this._props.closeMenu?.();
-            const batch = this._props.undoable !== false ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined;
+            const batch = this._props.undoable ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined;
             await this._props.event({ x: e.clientX, y: e.clientY });
             batch?.end();
         }
     };
 
-    currentTimeout?: NodeJS.Timeout | undefined;
-    static readonly timeout = 300;
-    _overPosY = 0;
-    _overPosX = 0;
+    setOverItem = (over: boolean) => {
+        this._hoverTimeout = setTimeout( action(() => {  this._overItem = over;  }), ContextMenuItem.HOVER_TIMEOUT ); // prettier-ignore
+    };
+
     onPointerEnter = (e: React.MouseEvent) => {
-        if (this.currentTimeout) {
-            clearTimeout(this.currentTimeout);
-            this.currentTimeout = undefined;
-        }
-        if (this.overItem) {
-            return;
-        }
+        this._hoverTimeout && clearTimeout(this._hoverTimeout);
         this._overPosY = e.clientY;
         this._overPosX = e.clientX;
-        this.currentTimeout = setTimeout(
-            action(() => {
-                this.overItem = true;
-            }),
-            ContextMenuItem.timeout
-        );
+        !this._overItem && this.setOverItem(true);
     };
 
     onPointerLeave = () => {
-        if (this.currentTimeout) {
-            clearTimeout(this.currentTimeout);
-            this.currentTimeout = undefined;
-        }
-        if (!this.overItem) {
-            return;
-        }
-        this.currentTimeout = setTimeout(
-            action(() => {
-                this.overItem = false;
-            }),
-            ContextMenuItem.timeout
-        );
+        this._hoverTimeout && clearTimeout(this._hoverTimeout);
+        this._overItem && this.setOverItem(false);
     };
 
-    isJSXElement(val: unknown): val is JSX.Element {
-        return React.isValidElement(val);
-    }
+    renderItem = (submenu: JSX.Element[]) => {
+        const alignItems = this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center';
+        const marginTop = this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : '';
+        const marginLeft = window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%';
 
-    render() {
-        if ('event' in this._props) {
-            return (
-                <div className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}>
-                    {this._props.icon ? <span className="contextMenu-item-icon-background">{this.isJSXElement(this._props.icon) ? this._props.icon : <FontAwesomeIcon icon={this._props.icon} size="sm" />}</span> : null}
-                    <div className="contextMenu-description">{this._props.description.replace(':', '')}</div>
-                    <div
-                        className="contextMenu-item-background"
-                        style={{
-                            background: SnappingManager.userColor,
-                        }}
-                    />
-                </div>
-            );
-        }
-        if ('subitems' in this._props) {
-            const where = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center';
-            const marginTop = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : '';
+        return (
+            <div className={`contextMenuItem${this._props.selected ? '-Selected' : ''}`} //
+                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} onPointerDown={this.handleEvent} 
+                 style={{ alignItems, borderTop: this._props.addDivider ? 'solid 1px' : undefined }}
+            >
+                <div className="contextMenuItem-background" style={{ background: SnappingManager.userColor, filter: this._overItem ? 'opacity(0.2)' : '' }} />
+                <span className="contextMenuItem-icon" style={{ alignItems: 'center', alignSelf: 'center' }}>
+                    {React.isValidElement(this._props.icon) ? this._props.icon : this._props.icon ? <FontAwesomeIcon icon={this._props.icon as IconProp} size="sm" /> : null}
+                </span>
+                <div className="contextMenu-description"> {this._props.description} </div>
+                {!submenu.length ? null : (
+                    !this._overItem ? 
+                        <FontAwesomeIcon icon="angle-right" size="lg" style={{ position: 'absolute', right: '10px' }} /> : (
+                        <div className="contextMenu-subMenu-cont" style={{ marginLeft, marginTop, background: SnappingManager.userBackgroundColor }}>
+                            {submenu}
+                        </div>
+                    )
+                )}
+            </div>
+        ); // prettier-ignore
+    };
 
-            // here
-            const submenu = !this.overItem ? null : (
-                <div
-                    className="contextMenu-subMenu-cont"
-                    style={{
-                        marginLeft: window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%',
-                        marginTop,
-                        background: SnappingManager.userBackgroundColor,
-                    }}>
-                    {this._items.map(prop => (
-                        <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} />
-                    ))}
-                </div>
-            );
-            if (!(this._props as SubmenuProps).noexpand) {
-                return (
-                    <div className="contextMenu-inlineMenu">
-                        {this._items.map(prop => (
-                            <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} />
-                        ))}
-                    </div>
-                );
-            }
-            return (
-                <div
-                    className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')}
-                    style={{ alignItems: where, borderTop: this._props.addDivider ? 'solid 1px' : undefined }}
-                    onMouseLeave={this.onPointerLeave}
-                    onMouseEnter={this.onPointerEnter}>
-                    {this._props.icon ? (
-                        <span className="contextMenu-item-icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: 'center', alignSelf: 'center' }}>
-                            <FontAwesomeIcon icon={this._props.icon} size="sm" />
-                        </span>
-                    ) : null}
-                    <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} style={{ alignItems: 'center', alignSelf: 'center' }}>
-                        {this._props.description}
-                        <FontAwesomeIcon icon="angle-right" size="lg" style={{ position: 'absolute', right: '10px' }} />
-                    </div>
-                    <div
-                        className="contextMenu-item-background"
-                        style={{
-                            background: SnappingManager.userColor,
-                        }}
-                    />
-                    {submenu}
-                </div>
-            );
-        }
-        return null;
+    render() {
+        const submenu = this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} />);
+        return this.props.event || this._props.noexpand ? this.renderItem(submenu) : <div className="contextMenu-inlineMenu">{submenu}</div>;
     }
 }
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
index b242acdba..e33049d3b 100644
--- a/src/client/views/DictationOverlay.tsx
+++ b/src/client/views/DictationOverlay.tsx
@@ -17,7 +17,7 @@ export class DictationOverlay extends React.Component {
     // eslint-disable-next-line react/no-unused-class-component-methods
     public hasActiveModal = false;
 
-    constructor(props: any) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         DictationOverlay.Instance = this;
diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts
index 1f5f29c7e..49a30aa08 100644
--- a/src/client/views/DocViewUtils.ts
+++ b/src/client/views/DocViewUtils.ts
@@ -6,6 +6,7 @@ import { Doc, SetActiveAudioLinker } from '../../fields/Doc';
 import { DocUtils } from '../documents/DocUtils';
 import { FieldViewProps } from './nodes/FieldView';
 
+// eslint-disable-next-line @typescript-eslint/no-namespace
 export namespace DocViewUtils {
     export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 487868169..b778a4fb9 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,6 +1,3 @@
-/* eslint-disable jsx-a11y/control-has-associated-label */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core';
 import { faCalendarDays } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -20,7 +17,7 @@ import { DictationManager } from '../util/DictationManager';
 import { DragManager } from '../util/DragManager';
 import { dropActionType } from '../util/DropActionTypes';
 import { SharingManager } from '../util/SharingManager';
-import { UndoManager, undoBatch } from '../util/UndoManager';
+import { UndoManager, undoable } from '../util/UndoManager';
 import './DocumentButtonBar.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import { PinProps } from './PinFuncs';
@@ -33,12 +30,12 @@ import { OpenWhere } from './nodes/OpenWhere';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
 
 @observer
-export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> {
+export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> {
     private _dragRef = React.createRef<HTMLDivElement>();
     // eslint-disable-next-line no-use-before-define
     public static Instance: DocumentButtonBar;
 
-    constructor(props: any) {
+    constructor(props: { views: () => (DocumentView | undefined)[]; stack?: unknown }) {
         super(props);
         makeObservable(this);
         DocumentButtonBar.Instance = this;
@@ -83,7 +80,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                 <div
                     className="documentButtonBar-icon documentButtonBar-follow"
                     style={{ backgroundColor: followLink ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: followLink ? Colors.BLACK : Colors.WHITE }}
-                    onClick={undoBatch(() => this._props.views().map(view => view?.toggleFollowLink(undefined, false)))}>
+                    onClick={undoable(() => this._props.views().map(view => view?.toggleFollowLink(undefined, false)), 'follow link')}>
                     <div className="documentButtonBar-followTypes">
                         {followBtn(
                             true,
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 93c3e3338..68970223a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -34,6 +34,7 @@ import { DocumentView } from './nodes/DocumentView';
 import { ImageBox } from './nodes/ImageBox';
 import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 
 interface DocumentDecorationsProps {
     PanelWidth: number;
@@ -145,7 +146,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     titleEntered = (e: React.KeyboardEvent) => {
         if (e.key === 'Enter') {
             e.stopPropagation();
-            (e.target as any).blur();
+            (e.target as HTMLElement).blur?.();
         }
     };
 
@@ -239,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             }
         });
         if (!this._iconifyBatch) {
-            (document.activeElement as any).blur?.();
+            (document.activeElement as HTMLElement).blur?.();
             this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying');
         } else {
             // eslint-disable-next-line no-param-reassign
@@ -254,7 +255,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [DocumentView.SelectedDocs().lastElement()]) ?? false, emptyFunction, this.onMaximizeClick, false, false);
         e.stopPropagation();
     };
-    onMaximizeClick = (e: any): void => {
+    onMaximizeClick = (e: PointerEvent): void => {
         const selView = DocumentView.Selected()[0];
         if (selView) {
             if (e.ctrlKey) {
@@ -349,8 +350,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(
             this,
             e,
-            (moveEv: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events
-                this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, 
+            (moveEv: PointerEvent, down: number[], delta: number[]) => {
+                this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]);
+                return false;
+            }, 
             action(() => { this._isRotating = false; }), // upEvent
             action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }),
             true
@@ -430,7 +433,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
         e.stopPropagation();
         const id = (this._resizeHdlId = e.currentTarget.className);
-        const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as any).width.replace('px', '')) / 2 : 0;
+        const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as HTMLElement).width?.replace('px', '')) / 2 : 0;
         const bounds = e.currentTarget.getBoundingClientRect();
         this._offset = {
             x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, //
@@ -478,7 +481,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y};
             DocumentView.Selected().forEach(docView => 
                 this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore
-            await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})});
+            await new Promise<void>(res => { setTimeout(() => { res(this._interactionLock = undefined)})});
         }); // prettier-ignore
 
         return false;
@@ -681,10 +684,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 const collectionAcl = docView.containerViewPath?.()?.lastElement() ? GetEffectiveAcl(docView.containerViewPath?.().lastElement().dataDoc) : AclEdit;
                 return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin;
             });
-        const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
+        const topBtn = (key: string, icon: IconProp, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: PointerEvent) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
-                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click!(clickEv)))}>
-                    <FontAwesomeIcon icon={icon as any} />
+                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click?.(clickEv)))}>
+                    <FontAwesomeIcon icon={icon} />
                 </div>
             </Tooltip>
         );
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 684b948af..e02e39b8b 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,10 +1,7 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as Autosuggest from 'react-autosuggest';
-import { ObjectField } from '../../fields/ObjectField';
 import './EditableView.scss';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
@@ -29,7 +26,7 @@ export interface EditableProps {
     /**
      * The contents to render when not editing
      */
-    contents: any;
+    contents: string;
     fieldContents?: FieldViewProps;
     fontStyle?: string;
     fontSize?: number;
@@ -41,8 +38,8 @@ export interface EditableProps {
     autosuggestProps?: {
         resetValue: () => void;
         value: string;
-        onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void;
-        autosuggestProps: Autosuggest.AutosuggestProps<string, any>;
+        onChange: (e: React.FormEvent, { newValue }: { newValue: string }) => void;
+        autosuggestProps: Autosuggest.AutosuggestProps<string, unknown>;
     };
     oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea
     allowCRs?: boolean; // can carriage returns be entered
@@ -112,8 +109,8 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
     }
 
     onChange = (e: React.ChangeEvent) => {
-        const targVal = (e.target as any).value;
-        if (!(targVal.startsWith(':=') || targVal.startsWith('='))) {
+        const targVal = (e.target as HTMLSelectElement).value;
+        if (!(targVal?.startsWith(':=') || targVal?.startsWith('='))) {
             this._overlayDisposer?.();
             this._overlayDisposer = undefined;
         } else if (!this._overlayDisposer) {
@@ -230,13 +227,11 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
                     className: 'editableView-input',
                     onKeyDown: this.onKeyDown,
                     autoFocus: true,
-                    // @ts-ignore
-                    onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false),
+                    onBlur: e => this.finalizeEdit((e.currentTarget as HTMLSelectElement).value, false, true, false),
                     onPointerDown: this.stopPropagation,
                     onClick: this.stopPropagation,
                     onPointerUp: this.stopPropagation,
                     value: this._props.autosuggestProps.value,
-                    // @ts-ignore
                     onChange: this._props.autosuggestProps.onChange,
                 }}
             />
@@ -248,7 +243,6 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
                 placeholder={this._props.placeholder}
                 onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
                 defaultValue={this._props.GetValue()}
-                // eslint-disable-next-line jsx-a11y/no-autofocus
                 autoFocus
                 onChange={this.onChange}
                 onKeyDown={this.onKeyDown}
@@ -264,7 +258,6 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
                 placeholder={this._props.placeholder}
                 onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
                 defaultValue={this._props.GetValue()}
-                // eslint-disable-next-line jsx-a11y/no-autofocus
                 autoFocus
                 onChange={this.onChange}
                 onKeyDown={this.onKeyDown}
@@ -288,7 +281,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
             );
         }
         setTimeout(() => this._props.autosuggestProps?.resetValue());
-        return this._props.contents instanceof ObjectField ? null : (
+        return (
             <div
                 className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}
                 ref={this._ref}
@@ -308,10 +301,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
                         fontStyle: this._props.fontStyle,
                         fontSize: this._props.fontSize,
                     }}>
-                    {
-                        // eslint-disable-next-line react/jsx-props-no-spreading
-                        this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this.props.contents ? this._props.contents?.valueOf() : ''
-                    }
+                    {this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this._props.contents ? this._props.contents?.valueOf() : ''}
                 </span>
             </div>
         );
diff --git a/src/client/views/ExtractColors.ts b/src/client/views/ExtractColors.ts
index f6928c52a..eee1d3a04 100644
--- a/src/client/views/ExtractColors.ts
+++ b/src/client/views/ExtractColors.ts
@@ -126,7 +126,7 @@ export class ExtractColors {
 
         let hue = 0;
         let saturation = 0;
-        let lightness = intensity;
+        const lightness = intensity;
 
         if (area !== 0) {
             saturation = area / (1 - Math.abs(2 * intensity - 1));
diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx
index 0ea0ebd83..407031b40 100644
--- a/src/client/views/FieldsDropdown.tsx
+++ b/src/client/views/FieldsDropdown.tsx
@@ -29,7 +29,7 @@ interface fieldsDropdownProps {
 @observer
 export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps> {
     @observable _newField = '';
-    constructor(props: any) {
+    constructor(props: fieldsDropdownProps) {
         super(props);
         makeObservable(this);
     }
@@ -101,13 +101,13 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps
                     }),
                 }}
                 placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()}
-                options={options as any}
+                options={options}
                 isMulti={false}
-                onChange={val => this._props.selectFunc((val as any as { value: string; label: string }).value)}
+                onChange={val => this._props.selectFunc((val as { value: string; label: string }).value)}
                 onKeyDown={e => {
                     if (e.key === 'Enter') {
                         runInAction(() => {
-                            this._props.selectFunc((this._newField = (e.nativeEvent.target as any)?.value));
+                            this._props.selectFunc((this._newField = (e.nativeEvent.target as HTMLSelectElement)?.value));
                         });
                     }
                     e.stopPropagation();
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 7d01bbabb..a85a03aab 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -56,7 +56,7 @@ export class KeyManager {
         window.addEventListener('keydown', KeyManager.Instance.handle);
         window.removeEventListener('keyup', KeyManager.Instance.unhandle);
         window.addEventListener('keyup', KeyManager.Instance.unhandle);
-        window.addEventListener('paste', KeyManager.Instance.paste as any);
+        window.addEventListener('paste', KeyManager.Instance.paste);
     }
 
     public unhandle = action((/* e: KeyboardEvent */) => {
@@ -330,7 +330,7 @@ export class KeyManager {
                 }
                 break;
             case 'c':
-                if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
+                if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
                     const bds = DocumentDecorations.Instance.Bounds;
                     const pt = DocumentView.Selected()[0]
                         .screenToViewTransform()
diff --git a/src/client/views/KeyphraseQueryView.scss b/src/client/views/KeyphraseQueryView.scss
deleted file mode 100644
index ac715e5e7..000000000
--- a/src/client/views/KeyphraseQueryView.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.fading {
-    animation: fanOut 1s
-}
-
-@keyframes fanOut {
-    from {opacity: 0;}
-    to {opacity: 1;}
-}
\ No newline at end of file
diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx
deleted file mode 100644
index 81f004010..000000000
--- a/src/client/views/KeyphraseQueryView.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/* eslint-disable jsx-a11y/label-has-associated-control */
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import './KeyphraseQueryView.scss';
-
-// tslint:disable-next-line: class-name
-export interface KP_Props {
-    keyphrases: string;
-}
-
-@observer
-export class KeyphraseQueryView extends React.Component<KP_Props> {
-    render() {
-        const keyterms = this.props.keyphrases.split(',');
-        return (
-            <div>
-                <h5>Select queries to send:</h5>
-                <form>
-                    {keyterms.map((kp: string) => (
-                        //  return (<p>{"-" + kp}</p>);
-                        <p>
-                            <label>
-                                <input name="query" type="radio" />
-                                <span>{kp}</span>
-                            </label>
-                        </p>
-                    ))}
-                </form>
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index a6dc5c62b..4a35805fb 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 /* eslint-disable react/require-default-props */
 import { isDark } from 'browndash-components';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx
index 34da82b6c..bb7a07f0e 100644
--- a/src/client/views/ObservableReactComponent.tsx
+++ b/src/client/views/ObservableReactComponent.tsx
@@ -8,27 +8,27 @@ import JsxParser from 'react-jsx-parser';
  * This is an abstract class that serves as the base for a PDF-style or Marquee-style
  * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
  */
-export abstract class ObservableReactComponent<T> extends React.Component<T, {}> {
+export abstract class ObservableReactComponent<T> extends React.Component<T, object> {
     @observable _props: React.PropsWithChildren<T>;
-    constructor(props: any) {
+    constructor(props: React.PropsWithChildren<T>) {
         super(props);
         this._props = props;
         makeObservable(this);
     }
     componentDidUpdate(prevProps: Readonly<T>): void {
         Object.keys(prevProps)
-            .filter(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey])
+            .filter(pkey => (prevProps as {[key:string]: unknown})[pkey] !== (this.props as  {[key:string]: unknown})[pkey])
             .forEach(action(pkey => {
-                (this._props as any)[pkey] = (this.props as any)[pkey];
+                (this._props as  {[key:string]: unknown})[pkey] = (this.props as  {[key:string]: unknown})[pkey];
             })); // prettier-ignore
     }
 }
 
 class ObserverJsxParser1 extends JsxParser {
-    constructor(props: any) {
+    constructor(props: object) {
         super(props);
-        observer(this as any);
+        observer(this as typeof JsxParser);
     }
 }
 
-export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
+export const ObserverJsxParser = ObserverJsxParser1 as typeof JsxParser;
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 034ade50b..7e597879d 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -7,13 +7,14 @@ import { Docs, DocumentOptions } from '../documents/Documents';
 import { DocUtils } from '../documents/DocUtils';
 import { ImageUtils } from '../util/Import & Export/ImageUtils';
 import { Transform } from '../util/Transform';
-import { UndoManager, undoBatch } from '../util/UndoManager';
+import { UndoManager, undoable } from '../util/UndoManager';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import './PreviewCursor.scss';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { StrCast } from '../../fields/Types';
 
 @observer
-export class PreviewCursor extends ObservableReactComponent<{}> {
+export class PreviewCursor extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static _instance: PreviewCursor;
     public static get Instance() {
@@ -29,7 +30,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     @observable _clickPoint: number[] = [];
     @observable public Visible = false;
     public Doc: Opt<Doc>;
-    constructor(props: any) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         PreviewCursor._instance = this;
@@ -46,7 +47,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
             });
 
             // tests for URL and makes web document
-            const re: any = /^https?:\/\//g;
+            const re = /^https?:\/\//g;
             const plain = e.clipboardData.getData('text/plain');
             if (plain && newPoint) {
                 // tests for youtube and makes video document
@@ -64,17 +65,19 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                 } else if (re.test(plain)) {
                     const url = plain;
                     if (!url.startsWith(window.location.href)) {
-                        undoBatch(() =>
-                            this._addDocument?.(
-                                Docs.Create.WebDocument(url, {
-                                    title: url,
-                                    _width: 500,
-                                    _height: 300,
-                                    data_useCors: true,
-                                    x: newPoint[0],
-                                    y: newPoint[1],
-                                })
-                            )
+                        undoable(
+                            () =>
+                                this._addDocument?.(
+                                    Docs.Create.WebDocument(url, {
+                                        title: url,
+                                        _width: 500,
+                                        _height: 300,
+                                        data_useCors: true,
+                                        x: newPoint[0],
+                                        y: newPoint[1],
+                                    })
+                                ),
+                            'paste web doc'
                         )();
                     } else alert('cannot paste dash into itself');
                 } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
@@ -94,11 +97,11 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
             }
             // pasting in images
             else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) {
-                const regEx: any = /<img src="(.*?)"/g;
-                const arr: any[] = regEx.exec(e.clipboardData.getData('text/html'));
+                const regEx = /<img src="(.*?)"/g;
+                const arr = regEx.exec(e.clipboardData.getData('text/html'));
 
-                if (newPoint) {
-                    undoBatch(() => {
+                if (newPoint && arr) {
+                    undoable(() => {
                         const doc = Docs.Create.ImageDocument(arr[1], {
                             _width: 300,
                             title: arr[1],
@@ -107,7 +110,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                         });
                         ImageUtils.ExtractImgInfo(doc);
                         this._addDocument?.(doc);
-                    })();
+                    }, 'paste image doc')();
                 }
             } else if (e.clipboardData.items.length && newPoint) {
                 const batch = UndoManager.StartBatch('collection view drop');
@@ -196,8 +199,12 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     }
     render() {
         return !this._clickPoint || !this.Visible ? null : (
-            // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
-            <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()} style={{ color: lightOrDark(this.Doc?.backgroundColor ?? 'white'), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}>
+            <div
+                className="previewCursor"
+                onBlur={this.onBlur}
+                tabIndex={0}
+                ref={e => e?.focus()}
+                style={{ color: lightOrDark(StrCast(this.Doc?.backgroundColor, 'white')), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}>
                 I
             </div>
         );
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index edf6df2b9..f346d4ba8 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 /* eslint-disable react/no-unused-class-component-methods */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components';
@@ -18,7 +16,7 @@ import { TfiBarChart } from 'react-icons/tfi';
 import { Doc, Opt } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, ScriptCast } from '../../fields/Types';
+import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
 import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
@@ -32,7 +30,7 @@ import { OpenWhere } from './nodes/OpenWhere';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 
 @observer
-export class PropertiesButtons extends React.Component<{}, {}> {
+export class PropertiesButtons extends React.Component {
     // eslint-disable-next-line no-use-before-define
     @observable public static Instance: PropertiesButtons;
 
@@ -314,13 +312,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //     );
     // }
 
-    @undoBatch
-    handlePerspectiveChange = (e: any) => {
-        this.selectedDoc && (this.selectedDoc._type_collection = e.target.value);
-        DocumentView.Selected().forEach(docView => {
-            docView.layoutDoc._type_collection = e.target.value;
-        });
-    };
     @computed get onClickVal() {
         const linkButton = IsFollowLinkScript(this.selectedDoc.onClick);
         const followLoc = this.selectedDoc._followLinkLocation;
@@ -452,7 +443,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick');
     };
 
-    propertyToggleBtn = (label: (on?: any) => string, property: string, tooltip: (on?: any) => string, icon: (on?: any) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
+    propertyToggleBtn = (label: (on?: unknown) => string, property: string, tooltip: (on?: unknown) => string, icon: (on?: unknown) => unknown, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedLayoutDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => {
             (dv?.layoutDoc || doc)[prop] = !(dv?.layoutDoc || doc)[prop];
@@ -463,7 +454,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 tooltip={tooltip(BoolCast(targetDoc[property]))}
                 text={label(targetDoc?.[property])}
                 color={SettingsManager.userColor}
-                icon={icon(targetDoc?.[property] as any)}
+                icon={icon(targetDoc?.[property]) as string}
                 iconPlacement="left"
                 align="flex-start"
                 fillWidth
@@ -484,7 +475,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isImage = layoutField instanceof ImageField;
         const isMap = this.selectedDoc?.type === DocumentType.MAP;
         const isCollection = this.selectedDoc?.type === DocumentType.COL;
-        const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(this.selectedDoc?._type_collection as any);
+        const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(StrCast(this.selectedDoc?._type_collection) as CollectionViewType);
         const isFreeForm = this.selectedDoc?._type_collection === CollectionViewType.Freeform;
         const isTree = this.selectedDoc?._type_collection === CollectionViewType.Tree;
         const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => (
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index edb55f341..e30d14eae 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -16,7 +16,7 @@ import { DocumentView } from './nodes/DocumentView';
 
 type PropertiesDocBacklinksSelectorProps = {
     Document: Doc;
-    Stack?: any;
+    Stack?: string;
     hideTitle?: boolean;
     addDocTab(doc: Doc, location: OpenWhere): void;
 };
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 1fea36d16..f494ff16a 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,6 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/anchor-is-valid */
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -15,14 +12,14 @@ import { OpenWhere } from './nodes/OpenWhere';
 
 type PropertiesDocContextSelectorProps = {
     DocView?: DocumentView;
-    Stack?: any;
+    Stack?: string;
     hideTitle?: boolean;
     addDocTab(doc: Doc, location: OpenWhere): void;
 };
 
 @observer
 export class PropertiesDocContextSelector extends ObservableReactComponent<PropertiesDocContextSelectorProps> {
-    constructor(props: any) {
+    constructor(props: PropertiesDocContextSelectorProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index b9a587719..12a46c7a4 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 /* eslint-disable react/require-default-props */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed } from 'mobx';
@@ -12,7 +10,7 @@ export interface PropertiesSectionProps {
     title: string;
     children?: JSX.Element | string | null;
     isOpen: boolean;
-    setIsOpen: (bool: boolean) => any;
+    setIsOpen: (bool: boolean) => void;
     onDoubleClick?: () => void;
 }
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 024db82a4..5952d6641 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,7 +1,4 @@
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable prettier/prettier */
-import { IconLookup } from '@fortawesome/fontawesome-svg-core';
+import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@mui/material';
@@ -12,6 +9,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorResult, SketchPicker } from 'react-color';
 import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import ResizeObserver from 'resize-observer-polyfill';
 import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
 import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
@@ -44,8 +42,6 @@ import { StyleProviderFuncType } from './nodes/FieldView';
 import { OpenWhere } from './nodes/OpenWhere';
 import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 
-const _global = (window /* browser */ || global) /* node */ as any;
-
 interface PropertiesViewProps {
     width: number;
     height: number;
@@ -59,7 +55,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
 
     // eslint-disable-next-line no-use-before-define
     public static Instance: PropertiesView | undefined;
-    constructor(props: any) {
+    constructor(props: PropertiesViewProps) {
         super(props);
         makeObservable(this);
         PropertiesView.Instance = this;
@@ -142,7 +138,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         return this.selectedDoc?.isGroup;
     }
     @computed get isStack() {
-        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any);
+        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as CollectionViewType);
     }
 
     rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20));
@@ -275,7 +271,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     @observable transform: Transform = Transform.Identity();
     getTransform = () => this.transform;
     propertiesDocViewRef = (ref: HTMLDivElement) => {
-        const resizeObserver = new _global.ResizeObserver(
+        const resizeObserver = new ResizeObserver(
             action(() => {
                 const cliRect = ref.getBoundingClientRect();
                 this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
@@ -357,7 +353,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
      * Handles the changing of a user's permissions from the permissions panel.
      */
     @undoBatch
-    changePermissions = (e: any, user: string) => {
+    changePermissions = (e: React.ChangeEvent<HTMLSelectElement>, user: string) => {
         const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc));
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
     };
@@ -456,7 +452,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     /**
      * Sorting algorithm to sort users.
      */
-    sortUsers = (u1: String, u2: String) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1);
+    sortUsers = (u1: string, u2: string) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1);
 
     /**
      * Sorting algorithm to sort groups.
@@ -711,7 +707,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         );
     }
 
-    inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => (
+    inputBox = (key: string, value: string | number | undefined, setter: (val: string) => void, title: string) => (
         <div
             className="inputBox"
             style={{
@@ -721,17 +717,29 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             <div className="inputBox-title"> {title} </div>
             <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} onKeyDown={e => e.stopPropagation()} />
             <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                <div
+                    className="inputBox-button-up"
+                    key="up2"
+                    onPointerDown={undoable(
+                        action(() => this.upDownButtons('up', key)),
+                        'down btn'
+                    )}>
                     <FontAwesomeIcon icon="caret-up" size="sm" />
                 </div>
-                <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                <div
+                    className="inputbox-Button-down"
+                    key="down2"
+                    onPointerDown={undoable(
+                        action(() => this.upDownButtons('down', key)),
+                        'up btn'
+                    )}>
                     <FontAwesomeIcon icon="caret-down" size="sm" />
                 </div>
             </div>
         </div>
     );
 
-    inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => (
+    inputBoxDuo = (key: string, value: string | number | undefined, setter: (val: string) => void, title1: string, key2: string, value2: string | number | undefined, setter2: (val: string) => void, title2: string) => (
         <div className="inputBox-duo">
             {this.inputBox(key, value, setter, title1)}
             {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)}
@@ -841,7 +849,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     @observable private _fillBtn = false;
     @observable private _lineBtn = false;
 
-    private _lastDash: any = '2';
+    private _lastDash: string = '2';
 
     @computed get colorFil() { return StrCast(this.selectedDoc?.[DocData].fillColor); } // prettier-ignore
     set colorFil(value) { this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); } // prettier-ignore
@@ -917,7 +925,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         );
     }
 
-    @computed get dashdStk() {  return this.selectedDoc?.stroke_dash || ''; } // prettier-ignore
+    @computed get dashdStk() {  return StrCast(this.selectedDoc?.stroke_dash); } // prettier-ignore
     set dashdStk(value) {
         value && (this._lastDash = value);
         this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined);
@@ -939,14 +947,26 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
     }
 
-    regInput = (key: string, value: any, setter: (val: string) => {}) => (
+    regInput = (key: string, value: string | number | undefined, setter: (val: string) => void) => (
         <div className="inputBox">
             <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} />
             <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                <div
+                    className="inputBox-button-up"
+                    key="up2"
+                    onPointerDown={undoable(
+                        action(() => this.upDownButtons('up', key)),
+                        'up'
+                    )}>
                     <FontAwesomeIcon icon="caret-up" size="sm" />
                 </div>
-                <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                <div
+                    className="inputbox-Button-down"
+                    key="down2"
+                    onPointerDown={undoable(
+                        action(() => this.upDownButtons('down', key)),
+                        'down'
+                    )}>
                     <FontAwesomeIcon icon="caret-down" size="sm" />
                 </div>
             </div>
@@ -1002,7 +1022,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                             className="arrows-head-input"
                             type="checkbox"
                             checked={this.markHead !== ''}
-                            onChange={undoBatch(action(() => {  this.markHead = this.markHead ? '' : 'arrow';  }))}
+                            onChange={undoable(action(() => {  this.markHead = this.markHead ? '' : 'arrow';  }), "change arrow head")}
                         />
                     </div>
                     <div className="arrows-tail">
@@ -1012,8 +1032,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                             className="arrows-tail-input"
                             type="checkbox"
                             checked={this.markTail !== ''}
-                            onChange={undoBatch(
-                                action(() => { this.markTail = this.markTail ? '' : 'arrow'; })
+                            onChange={undoable(
+                                action(() => { this.markTail = this.markTail ? '' : 'arrow'; }) ,"change arrow tail"
                             )}
                         />
                     </div>
@@ -1044,7 +1064,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     setFinalNumber = () => {
         this._sliderBatch?.end();
     };
-    getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any, autorange?: number, autorangeMinVal?: number) => (
+
+    getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: (val: number) => void, autorange?: number, autorangeMinVal?: number) => (
         <div key={label + (this.selectedDoc?.title ?? '')}>
             <NumberInput formLabel={label} formLabelPlacement="left" type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} />
             <Slider
@@ -1234,7 +1255,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     }
 
     @computed get description() {
-        return Field.toString(this.selectedLink?.link_description as any as FieldType);
+        return Field.toString(this.selectedLink?.link_description as FieldType);
     }
     @computed get relationship() {
         return StrCast(this.selectedLink?.link_relationship);
@@ -1332,7 +1353,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                 <div
                     style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '20%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', background: color, color: 'black' }}
                     onClick={() => this.changeEffectDirection(direction)}>
-                    {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
+                    {icon ? <FontAwesomeIcon icon={icon as IconProp} /> : null}
                 </div>
             </Tooltip>
         );
@@ -1368,7 +1389,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             e,
             returnFalse,
             emptyFunction,
-            undoBatch(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); })) // prettier-ignore
+            undoable(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); }), `toggle prop: ${prop}`) // prettier-ignore
         );
     };
 
@@ -1385,17 +1406,17 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         return selAnchor ?? (this.selectedLink && this.destinationAnchor ? Doc.getOppositeAnchor(this.selectedLink, this.destinationAnchor) : this.selectedLink);
     }
 
-    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: any = true, ovalue: any = false, cb: (val: any) => any = val => val) => {
+    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: FieldType = true, ovalue: FieldType = false, cb: (val: FieldType) => void = val => val) => {
         anchor &&
             setupMoveUpEvents(
                 this,
                 e,
                 returnFalse,
                 emptyFunction,
-                undoBatch(action(() => {
+                undoable(action(() => {
                     anchor[prop] = anchor[prop] === value ? ovalue : value;
-                    this.selectedDoc && cb(anchor[prop]);
-                })) // prettier-ignore
+                    this.selectedDoc && cb(anchor[prop] as boolean);
+                }), `toggle anchor prop: ${prop}`) // prettier-ignore
             );
     };
 
@@ -1433,7 +1454,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     }
 
     // Converts seconds to ms and updates presTransition
-    setZoom = (number: String, change?: number) => {
+    setZoom = (number: string, change?: number) => {
         let scale = Number(number) / 100;
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
@@ -1530,7 +1551,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Play Target Audio</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
@@ -1544,7 +1564,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Play Target Video</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !this.sourceAnchor?.followLinkVideo ? '' : '#4476f7', borderRadius: 3 }}
@@ -1558,7 +1577,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Zoom Text Selections</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
@@ -1572,7 +1590,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Toggle Follow to Outer Context</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
@@ -1586,7 +1603,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Toggle Target (Show/Hide)</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
@@ -1600,7 +1616,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Ease Transitions</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
@@ -1614,7 +1629,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Capture Offset to Target</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
@@ -1631,7 +1645,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         <div className="propertiesView-input inline">
                             <p>Center Target (no zoom)</p>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
@@ -1647,16 +1660,15 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                             <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
                                 <input className="presBox-input" style={{ width: '100%', color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} readOnly type="number" value={zoom} />
                                 <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+                                    <div className="ribbon-propertyUpDownItem" onClick={undoable(() => this.setZoom(String(zoom), 0.1), 'Zoom out')}>
                                         <FontAwesomeIcon icon="caret-up" />
                                     </div>
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+                                    <div className="ribbon-propertyUpDownItem" onClick={undoable(() => this.setZoom(String(zoom), -0.1), 'Zoom in')}>
                                         <FontAwesomeIcon icon="caret-down" />
                                     </div>
                                 </div>
                             </div>
                             {
-                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                 <button
                                     type="button"
                                     style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
@@ -1746,8 +1758,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         }
         if (this.isPres && PresBox.Instance) {
             const selectedItem: boolean = PresBox.Instance.selectedArray.size > 0;
-            const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType)
-                ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType)
+            const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
+                ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
                 : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
             return (
                 <div className="propertiesView" style={{ width: this._props.width }}>
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 9b70f1ca7..8f0a35df0 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -40,7 +38,7 @@ interface ExtraProps {
 }
 @observer
 export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & ExtraProps> {
-    constructor(props: any) {
+    constructor(props: FieldViewProps & ExtraProps) {
         super(props);
         makeObservable(this);
     }
@@ -85,7 +83,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         });
         Doc.SetSelectOnLoad(target);
         FormattedTextBox.DontSelectInitialText = true;
-        const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
+        DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
 
         const taggedContent = this.childFilters()
             .filter(data => data.split(':')[0])
@@ -102,7 +100,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             });
 
         if (!anchor.text) anchor[DocData].text = '-selection-';
-        const textLines: any = [
+        const textLines: { type: string; attrs: object; content?: unknown[] }[] = [
             {
                 type: 'paragraph',
                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
@@ -222,7 +220,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                     pointerEvents: this._props.isContentActive() ? 'all' : undefined,
                     top: this._props.Document.type !== DocumentType.RTF && StrCast(this._props.Document._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
-                    background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor),
+                    background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor) as string,
                     width: `100%`,
                     height: '100%',
                 }}>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index b111904f3..2792955a0 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -17,7 +17,7 @@ import { emptyPath } from '../../Utils';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { IsFollowLinkScript } from '../documents/DocUtils';
 import { SnappingManager } from '../util/SnappingManager';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, UndoManager } from '../util/UndoManager';
 import { TreeSort } from './collections/TreeSort';
 import { Colors } from './global/globalEnums';
 import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
@@ -105,8 +105,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
     const fieldKey = fieldKeyProp ? fieldKeyProp + '_' : isCaption ? 'caption_' : '';
     const isInk = () => layoutDoc?._layout_isSvg && !LayoutTemplateString;
     const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
-    const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight);
-    const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1));
+    const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight) as number;
+    const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)) as string;
     const color = () => styleProvider?.(doc, props, StyleProp.Color) as string;
     const opacity = () => styleProvider?.(doc, props, StyleProp.Opacity);
     const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle) as string;
@@ -383,11 +383,12 @@ export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp,
             size={Size.XSMALL}
             color={color}
             icon={<FontAwesomeIcon icon={doc[field] ? onIcon : offIcon} />}
-            onClick={undoBatch(
+            onClick={undoable(
                 action((e: React.MouseEvent) => {
                     e.stopPropagation();
                     clickFunc ? clickFunc() : (doc[field] = doc[field] ? undefined : true);
-                })
+                }),
+                'toggle dashboard feature'
             )}
         />
     );
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index cff32a557..e0d0de165 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -48,7 +48,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             .forEach(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); // prettier-ignore
     }
     @computed get scriptField() {
-        const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.Document) as any });
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.Document) as any }); // allow a captured variable for Doc[] since this script isn't being saved to a Doc
         return script ? () => script : undefined;
     }
 
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 2d461c0ab..9b71d46ea 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { Tooltip } from '@mui/material';
 import { Popup, Type } from 'browndash-components';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index c969c39e5..4bec2d963 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -10,7 +10,6 @@ import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
@@ -135,7 +134,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         const cm = ContextMenu.Instance;
 
         const revealOptions = cm.findByDescription('Filter Flashcards');
-        const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
+        const revealItems = revealOptions?.subitems ?? [];
         revealItems.push({description: 'All',           event: () => {this.layoutDoc.filterOp = undefined;},         icon: 'layer-group',}); // prettier-ignore
         revealItems.push({description: 'Star',          event: () => {this.layoutDoc.filterOp = cardMode.STAR;},     icon: 'star',}); // prettier-ignore
         revealItems.push({description: 'Practice Mode', event: () => {this.layoutDoc.filterOp = cardMode.PRACTICE;}, icon: 'check',}); // prettier-ignore
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 7b9475590..f1b5e4db2 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -19,7 +19,7 @@ import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, undoable } from '../../util/UndoManager';
 import { AntimodeMenu } from '../AntimodeMenu';
 import { EditableView } from '../EditableView';
 import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
@@ -181,7 +181,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         params: ['target', 'source'],
         title: 'item view',
         script: 'this.target.childLayoutTemplate = getDocTemplate(this.source?.[0])',
-        immediate: undoBatch((source: Doc[]) => {
+        immediate: undoable((source: Doc[]) => {
             let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
             try {
                 formatStr && JSON.parse(formatStr);
@@ -196,25 +196,25 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                 Doc.SetInPlace(this.target, 'childLayoutString', undefined, true);
                 Doc.SetInPlace(this.target, 'childLayoutTemplate', undefined, true);
             }
-        }),
+        }, ''),
         initialize: emptyFunction,
     };
     _narrativeCommand = {
         params: ['target', 'source'],
         title: 'child click view',
         script: 'this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])',
-        immediate: undoBatch((source: Doc[]) => {
+        immediate: undoable((source: Doc[]) => {
             source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]));
-        }),
+        }, 'narrative command'),
         initialize: emptyFunction,
     };
     _contentCommand = {
         params: ['target', 'source'],
         title: 'set content',
         script: 'getProto(this.target).data = copyField(this.source);',
-        immediate: undoBatch((source: Doc[]) => {
+        immediate: undoable((source: Doc[]) => {
             this.target[DocData].data = new List<Doc>(source);
-        }),
+        }, ''),
         initialize: emptyFunction,
     };
     _onClickCommand = {
@@ -225,19 +225,19 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
              getProto(this.proxy[0]).target = this.target.target;
              getProto(this.proxy[0]).source = copyField(this.target.source); 
             }}`,
-        immediate: undoBatch(() => {}),
+        immediate: undoable(() => {}, ''),
         initialize: emptyFunction,
     };
     _viewCommand = {
         params: ['target'],
         title: 'bookmark view',
         script: "this.target._freeform_panX = this.target_freeform_panX; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);",
-        immediate: undoBatch(() => {
+        immediate: undoable(() => {
             this.target._freeform_panX = 0;
             this.target._freeform_panY = 0;
             this.target._freeform_scale = 1;
             this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
-        }),
+        }, ''),
         initialize: (button: Doc) => {
             button['target-panX'] = this.target._freeform_panX;
             button['target-panY'] = this.target._freeform_panY;
@@ -249,18 +249,18 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         params: ['target'],
         title: 'fit content',
         script: 'this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;',
-        immediate: undoBatch(() => {
+        immediate: undoable(() => {
             this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;
-        }),
+        }, ''),
         initialize: emptyFunction,
     };
     _fitContentCommand = {
         params: ['target'],
         title: 'toggle clusters',
         script: 'this.target._freeform_useClusters = !this.target._freeform_useClusters;',
-        immediate: undoBatch(() => {
+        immediate: undoable(() => {
             this.target._freeform_useClusters = !this.target._freeform_useClusters;
-        }),
+        }, ''),
         initialize: emptyFunction,
     };
     _saveFilterCommand = {
@@ -268,10 +268,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         title: 'save filter',
         script: `this.target._childFilters = compareLists(this.target_childFilters,this.target._childFilters) ? undefined : copyField(this.target_childFilters); 
         this.target._searchFilterDocs = compareLists(this.target_searchFilterDocs,this.target._searchFilterDocs) ? undefined: copyField(this.target_searchFilterDocs);`,
-        immediate: undoBatch(() => {
+        immediate: undoable(() => {
             this.target._childFilters = undefined;
             this.target._searchFilterDocs = undefined;
-        }),
+        }, ''),
         initialize: (button: Doc) => {
             const activeDash = Doc.ActiveDashboard;
             if (activeDash) {
@@ -594,9 +594,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
      */
     onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         if (e.currentTarget.valueAsNumber > 0)
-            undoBatch(() => {
+            undoable(() => {
                 this.document.gridNumCols = e.currentTarget.valueAsNumber;
-            })();
+            }, '')();
     };
 
     /**
@@ -625,9 +625,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     onIncrementButtonClick = () => {
         this.clicked = true;
         this.entered && (this.document.gridNumCols as number)--;
-        undoBatch(() => {
+        undoable(() => {
             this.document.gridNumCols = this.numCols + 1;
-        })();
+        }, '')();
         this.entered = false;
     };
 
@@ -638,9 +638,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         this.clicked = true;
         if (this.numCols > 1 && !this.decrementLimitReached) {
             this.entered && (this.document.gridNumCols as number)++;
-            undoBatch(() => {
+            undoable(() => {
                 this.document.gridNumCols = this.numCols - 1;
-            })();
+            }, '')();
             if (this.numCols === 1) this.decrementLimitReached = true;
         }
         this.entered = false;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ff316cfbd..0557a9102 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -213,7 +213,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
         if (!Doc.noviceMode) {
             const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
-            const onClicks: ContextMenuProps[] = existingOnClick.subitems ?? [];
+            const onClicks: ContextMenuProps[] = existingOnClick?.subitems ?? [];
             onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
             !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
         }
@@ -226,7 +226,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     get editableTitle() {
         return (
             <EditableView
-                contents={this.dataDoc.title}
+                contents={StrCast(this.dataDoc.title)}
                 display="block"
                 maxHeight={72}
                 height="auto"
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5c304b4a9..c9ab5f661 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -48,7 +48,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
     private reactionDisposer: IReactionDisposer | undefined;
     @observable _isContentActive: boolean | undefined = undefined;
 
-    constructor(props: any) {
+    constructor(props: CollectionViewProps) {
         super(props);
         makeObservable(this);
         this._annotationKeySuffix = returnEmptyString;
@@ -72,7 +72,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
     }
 
     get collectionViewType(): CollectionViewType | undefined {
-        const viewField = StrCast(this.layoutDoc._type_collection) as any as CollectionViewType;
+        const viewField = StrCast(this.layoutDoc._type_collection) as CollectionViewType;
         if (CollectionView._safeMode) {
             switch (viewField) { 
                 case CollectionViewType.Freeform:
@@ -132,7 +132,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
             ];
 
             const existingVm = ContextMenu.Instance.findByDescription(category);
-            const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
+            const catItems = existingVm?.subitems ?? [];
             catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
             !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' });
         }
@@ -151,7 +151,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
                 });
 
             const options = cm.findByDescription('Options...');
-            const optionItems = options && 'subitems' in options ? options.subitems : [];
+            const optionItems = options?.subitems ?? [];
             !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => {this.Document.forceActive = !this.Document.forceActive}, icon: 'project-diagram' }) : null; // prettier-ignore
             if (this.Document.childLayout instanceof Doc) {
                 optionItems.push({ description: 'View Child Layout', event: () => this._props.addDocTab(this.Document.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' });
@@ -165,7 +165,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
 
             if (!Doc.noviceMode && !this.Document.annotationOn && !this._props.hideClickBehaviors) {
                 const existingOnClick = cm.findByDescription('OnClick...');
-                const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+                const onClicks = existingOnClick?.subitems ?? [];
                 const funcs = [
                     { key: 'onChildClick', name: 'On Child Clicked' },
                     { key: 'onChildDoubleClick', name: 'On Child Double Clicked' },
@@ -195,7 +195,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
 
             if (!Doc.noviceMode) {
                 const more = cm.findByDescription('More...');
-                const moreItems = more && 'subitems' in more ? more.subitems : [];
+                const moreItems = more?.subitems ?? [];
                 moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.Document) });
                 !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
             }
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 2b7de5082..2c7920bdd 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,7 +1,8 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
 import { Colors } from 'browndash-components';
 import { action, runInAction } from 'mobx';
 import { aggregateBounds } from '../../../Utils';
-import { Doc, DocListCast, NumListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, FieldType, NumListCast, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -138,7 +139,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
     const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4', 
     { 
         waitForRender?: boolean; 
-        checkResult: (doc: Doc) => any; 
+        checkResult: (doc: Doc) => boolean; 
         setDoc: (doc: Doc, dv: DocumentView) => void; 
     }> = new Map([
         ['grid', {
@@ -220,6 +221,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
         }],
     ]);
     for (let i = 0; i < 8; i++) {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
         map.set((i + 1 + '') as any, {
             checkResult: (doc: Doc) => NumListCast(doc?.cardSort_visibleSortGroups).includes(i),
             setDoc: (doc: Doc, dv: DocumentView) => {
@@ -278,25 +280,25 @@ ScriptingGlobals.add(function cardHasLabel(label: string) {
 // });
 
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: any, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     // prettier-ignore
-    const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
-            setDoc: () => value && RichTextMenu.Instance?.setFontField(value, 'fontFamily'),
+            setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontFamily'),
         }],
         ['highlight', {
             checkResult: () => RichTextMenu.Instance?.fontHighlight,
-            setDoc: () => value  && RichTextMenu.Instance?.setFontField(value, 'fontHighlight'),
+            setDoc: () => value  && RichTextMenu.Instance?.setFontField(value.toString(), 'fontHighlight'),
         }],
         ['fontColor', {
             checkResult: () => RichTextMenu.Instance?.fontColor,
-            setDoc: () => value && RichTextMenu.Instance?.setFontField(value, 'fontColor'),
+            setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontColor'),
         }],
         ['alignment', {
             checkResult: () => RichTextMenu.Instance?.textAlign,
-            setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value); },
+            setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value.toString() as "center"|"left"|"right"):(Doc.UserDoc().textAlign = value); },
         }],
         ['fontSize', {
             checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
@@ -317,7 +319,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
 });
 
 type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
-type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => any }];
+type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => unknown }];
 
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
@@ -438,7 +440,7 @@ function setActiveTool(tool: InkTool | Gestures, keepPrim: boolean, checkResult?
         if (GestureOverlay.Instance) {
             GestureOverlay.Instance.KeepPrimitiveMode = keepPrim;
         }
-        if (Object.values(Gestures).includes(tool as any)) {
+        if (Object.values(Gestures).includes(tool as Gestures)) {
             if (GestureOverlay.Instance.InkShape === tool && !keepPrim) {
                 Doc.ActiveTool = InkTool.None;
                 GestureOverlay.Instance.InkShape = undefined;
@@ -450,14 +452,14 @@ function setActiveTool(tool: InkTool | Gestures, keepPrim: boolean, checkResult?
             if (Doc.UserDoc().ActiveTool === tool) {
                 Doc.ActiveTool = InkTool.None;
             } else {
-                if ([InkTool.StrokeEraser, InkTool.RadiusEraser, InkTool.SegmentEraser].includes(tool as any)) {
+                if ([InkTool.StrokeEraser, InkTool.RadiusEraser, InkTool.SegmentEraser].includes(tool as InkTool)) {
                     Doc.UserDoc().activeEraserTool = tool;
                 }
                 // pen or eraser
                 if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) {
                     Doc.ActiveTool = InkTool.None;
                 } else {
-                    Doc.ActiveTool = tool as any;
+                    Doc.ActiveTool = tool as InkTool;
                     GestureOverlay.Instance.InkShape = undefined;
                 }
             }
@@ -476,10 +478,10 @@ ScriptingGlobals.add(function activeEraserTool() {
 
 // toggle: Set overlay status of selected document
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: any, checkResult?: boolean) {
+ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: string | number, checkResult?: boolean) {
     const selected = DocumentView.SelectedDocs().lastElement() ?? Doc.UserDoc();
     // prettier-ignore
-    const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
+    const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
         ['inkMask', {
             checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())),
             setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; },
@@ -508,7 +510,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil
         [ 'eraserWidth', {
             checkResult: () => ActiveEraserWidth(),
             setInk: (doc: Doc) => { },
-            setMode: () => { SetEraserWidth(value.toString());},
+            setMode: () => { SetEraserWidth(+value);},
         }]
     ]);
 
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 12b83414c..b38213e08 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -24,7 +24,7 @@ interface Props {
 export class LinkMenu extends ObservableReactComponent<Props> {
     _editorRef = React.createRef<HTMLDivElement>();
     @observable _linkMenuRef = React.createRef<HTMLDivElement>();
-    constructor(props: any) {
+    constructor(props: Props) {
         super(props);
         makeObservable(this);
     }
@@ -40,7 +40,7 @@ export class LinkMenu extends ObservableReactComponent<Props> {
 
     onPointerDown = action((e: PointerEvent) => {
         LinkInfo.Clear();
-        if (!this._linkMenuRef.current?.contains(e.target as any) && !this._editorRef.current?.contains(e.target as any)) {
+        if (!this._linkMenuRef.current?.contains(e.target as HTMLElement) && !this._editorRef.current?.contains(e.target as HTMLElement)) {
             this.clear();
         }
     });
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index cd735318e..c15508669 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 /* eslint-disable react/require-default-props */
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 9ce04ffac..f54d8311d 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable } from 'mobx';
@@ -15,7 +13,7 @@ import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { LinkManager } from '../../util/LinkManager';
 import { SnappingManager } from '../../util/SnappingManager';
-import { undoBatch } from '../../util/UndoManager';
+import { undoable } from '../../util/UndoManager';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
 import { LinkInfo } from '../nodes/LinkDocPreview';
@@ -56,7 +54,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
 export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
     _editRef = React.createRef<HTMLDivElement>();
-    constructor(props: any) {
+    constructor(props: LinkMenuItemProps) {
         super(props);
         makeObservable(this);
     }
@@ -123,7 +121,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
             this,
             e,
             moveEv => {
-                const eleClone: any = this._drag.current?.cloneNode(true);
+                const eleClone = this._drag.current?.cloneNode(true) as HTMLElement;
                 if (eleClone) {
                     eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`;
                     StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]);
@@ -151,7 +149,17 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
         );
     };
 
-    deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Doc.DeleteLink?.(this._props.linkDoc))));
+    deleteLink = (e: React.PointerEvent): void =>
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoable(
+                action(() => Doc.DeleteLink?.(this._props.linkDoc)),
+                'delete link'
+            )
+        );
     @observable _hover = false;
     docView = () => this._props.docView;
     render() {
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 69277b054..df6e74d85 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -429,7 +429,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     specificContextMenu = (): void => {
         const cm = ContextMenu.Instance;
         const options = cm.findByDescription('Options...');
-        const optionItems = options && 'subitems' in options ? options.subitems : [];
+        const optionItems = options?.subitems ?? [];
         optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
     };
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 8a2c4e530..b178d6554 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -89,8 +89,8 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
     /**
      * Set of all available rendering componets for Docs (e.g., ImageBox, CollectionFreeFormView, etc)
      */
-    private static Components: { [key: string]: DocumentContentsViewProps };
-    public static Init(defaultLayoutString: string, components: { [key: string]: DocumentContentsViewProps }) {
+    private static Components: { [key: string]: unknown };
+    public static Init(defaultLayoutString: string, components: { [key: string]: unknown }) {
         DocumentContentsView.DefaultLayoutString = defaultLayoutString;
         DocumentContentsView.Components = components;
     }
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 3d1bd7563..6b439cd64 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -1,4 +1,4 @@
-import functionPlot from 'function-plot';
+import functionPlot, { Chart } from 'function-plot';
 import { computed, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -22,11 +22,11 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         return FieldView.LayoutString(FunctionPlotBox, fieldKey);
     }
     public static GraphCount = 0;
-    _plot: any;
+    _plot: Chart | undefined;
     _plotId = '';
-    _plotEle: any;
+    _plotEle: HTMLDivElement | null = null;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
@@ -42,8 +42,10 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
         PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document);
-        anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
-        anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
+        if (this._plot) {
+            anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis?.domain ?? []));
+            anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis?.domain ?? []));
+        }
         if (addAsAnnotation) this.addDocument(anchor);
         return anchor;
     };
@@ -68,9 +70,9 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         const width = this._props.PanelWidth();
         const height = this._props.PanelHeight();
         try {
-            this._plotEle.children.length && this._plotEle.removeChild(this._plotEle.children[0]);
+            this._plotEle?.children.length && this._plotEle.removeChild(this._plotEle.children[0]);
             this._plot = functionPlot({
-                target: '#' + this._plotEle.id,
+                target: '#' + this._plotEle?.id,
                 width,
                 height,
                 xAxis: { domain: Cast(this.layoutDoc.xRange, listSpec('number'), [-10, 10]) },
@@ -104,7 +106,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         return false;
     };
 
-    _dropDisposer: any;
+    _dropDisposer: DragManager.DragDropDisposer | undefined;
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         if (ele) {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 9e77e0973..95e344004 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -14,7 +14,6 @@ import { SetupDrag } from '../../util/DragManager';
 import { CompiledScript } from '../../util/Scripting';
 import { undoable } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { DocumentIconContainer } from './DocumentIcon';
 import { FieldView, FieldViewProps } from './FieldView';
@@ -296,7 +295,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
     specificContextMenu = (): void => {
         const cm = ContextMenu.Instance;
         const open = cm.findByDescription('Change Perspective...');
-        const openItems: ContextMenuProps[] = open && 'subitems' in open ? open.subitems : [];
+        const openItems = open?.subitems ?? [];
         openItems.push({
             description: 'Default Perspective',
             event: () => {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 3023716b1..8e74e1ca2 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -110,7 +110,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                 <td className="keyValuePair-td-value" style={{ width: `${100 - this._props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
                     <div className="keyValuePair-td-value-container">
                         <EditableView
-                            contents={undefined}
+                            contents={''}
                             fieldContents={{
                                 Document: this._props.doc,
                                 childFilters: returnEmptyFilter,
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c03694dd9..b17275a1e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -22,7 +22,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 import { CollectionStackingView } from '../collections/CollectionStackingView';
 import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
 import { PDFViewer } from '../pdf/PDFViewer';
@@ -398,6 +397,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 </button>
             </>
         );
+
         const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
         const curPage = NumCast(this.Document._layout_curPage) || 1;
         return !this._props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
@@ -473,14 +473,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     specificContextMenu = (): void => {
         const cm = ContextMenu.Instance;
         const options = cm.findByDescription('Options...');
-        const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+        const optionItems = options?.subitems ?? [];
 
         !Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
         !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
         // optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
         !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' });
         const help = cm.findByDescription('Help...');
-        const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
+        const helpItems = help?.subitems ?? [];
         helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && ClientUtils.CopyText(ClientUtils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
         !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' });
     };
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 4d0e9efee..df1421a33 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -17,7 +17,7 @@ interface IEquationViewInternal {
     tbox: FormattedTextBox;
     width: number;
     height: number;
-    getPos: () => number;
+    getPos: () => number | undefined;
     setEditor: (editor: EquationEditor | undefined) => void;
 }
 
@@ -47,7 +47,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
                 className="equationView"
                 onKeyDown={e => {
                     if (e.key === 'Enter') {
-                        this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos() + 1))));
+                        this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve((this.props.getPos() ?? 0) + 1))));
                         this.props.tbox.EditorView!.focus();
                         e.preventDefault();
                     }
@@ -82,8 +82,8 @@ export class EquationView {
     tbox: FormattedTextBox;
     view: EditorView;
     _editor: EquationEditor | undefined;
-    getPos: () => number;
-    constructor(node: Node, view: EditorView, getPos: () => number, tbox: FormattedTextBox) {
+    getPos: () => number | undefined;
+    constructor(node: Node, view: EditorView, getPos: () => number | undefined, tbox: FormattedTextBox) {
         this.tbox = tbox;
         this.view = view;
         this.getPos = getPos;
@@ -109,7 +109,7 @@ export class EquationView {
         this._editor?.mathField.focus();
     }
     selectNode() {
-        this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos()))));
+        this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos() ?? 0))));
         this.tbox._applyingChange = this.tbox.fieldKey; // setting focus will make prosemirror lose focus, which will cause it to change its selection to a text selection, which causes this view to get rebuilt but it's no longer node selected, so the equationview won't have focus
         setTimeout(() => {
             this._editor?.mathField.focus();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c8b25e184..478039ffa 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -875,7 +875,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             })
         );
         const appearance = cm.findByDescription('Appearance...');
-        const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
+        const appearanceItems = appearance?.subitems ?? [];
 
         appearanceItems.push({
             description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle',
@@ -930,7 +930,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
 
         const options = cm.findByDescription('Options...');
-        const optionItems = options && 'subitems' in options ? options.subitems : [];
+        const optionItems = options?.subitems ?? [];
         optionItems.push({
             description: `Toggle auto update from template`,
             event: () => {
@@ -959,7 +959,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         const help = cm.findByDescription('Help...');
-        const helpItems = help && 'subitems' in help ? help.subitems : [];
+        const helpItems = help?.subitems ?? [];
         helpItems.push({ description: `show markdown options`, event: () => RTFMarkup.Instance.setOpen(true), icon: <BsMarkdownFill /> });
         !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' });
     };
-- 
cgit v1.2.3-70-g09d2


From b84bdc5a629dfa6310b24dd5eedee2843558b73a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 12 Aug 2024 21:38:22 -0400
Subject: more any -> type fixes

---
 deploy/mobile/image.html                           |  16 -
 deploy/mobile/ink.html                             |  13 -
 src/.DS_Store                                      | Bin 10244 -> 10244 bytes
 src/ClientUtils.ts                                 |   4 -
 src/ServerUtils.ts                                 |   1 +
 src/client/util/Scripting.ts                       |   2 +-
 src/client/util/type_decls.txt                     | 224 ++++++
 src/client/views/EditableView.tsx                  |   4 +-
 src/client/views/GestureOverlay.tsx                |   7 +-
 src/client/views/LightboxView.tsx                  |  12 +-
 src/client/views/MainView.tsx                      |  12 +-
 src/client/views/OverlayView.tsx                   |   8 +-
 src/client/views/PropertiesView.tsx                |   8 +-
 src/client/views/TemplateMenu.tsx                  |   4 +-
 src/client/views/collections/CollectionMenu.tsx    |   4 +-
 .../collections/CollectionStackedTimeline.tsx      |   4 +-
 .../views/collections/CollectionStackingView.tsx   |   5 +-
 .../CollectionStackingViewFieldColumn.tsx          |  11 +-
 .../views/collections/CollectionTreeView.tsx       |   7 +-
 src/client/views/collections/TabDocView.tsx        |  14 +-
 src/client/views/collections/TreeView.tsx          |   4 +-
 .../CollectionMulticolumnView.tsx                  |   8 +-
 .../CollectionMultirowView.tsx                     |   6 +-
 .../collectionMulticolumn/MulticolumnResizer.tsx   |   2 +-
 .../collectionMulticolumn/MultirowResizer.tsx      |   2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  57 +-
 .../collectionSchema/SchemaColumnHeader.tsx        |   4 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   4 +-
 .../collectionSchema/SchemaTableCell.tsx           |  32 +-
 src/client/views/linking/LinkPopup.tsx             |   4 +-
 src/client/views/newlightbox/NewLightboxView.tsx   |  14 +-
 .../RecommendationList/RecommendationList.tsx      |   7 +-
 src/client/views/nodes/KeyValuePair.tsx            |   4 +-
 src/client/views/nodes/LabelBox.tsx                |   5 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   7 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |  13 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  18 +-
 src/client/views/topbar/TopBar.tsx                 |   8 +-
 src/fields/Doc.ts                                  |   4 +
 src/mobile/ImageUpload.scss                        | 139 ----
 src/mobile/ImageUpload.tsx                         | 170 ----
 src/mobile/InkControls.tsx                         |   0
 src/mobile/MobileInkOverlay.scss                   |  39 -
 src/mobile/MobileInkOverlay.tsx                    | 183 -----
 src/mobile/MobileInterface.scss                    | 445 -----------
 src/mobile/MobileInterface.tsx                     | 871 ---------------------
 src/mobile/MobileMain.tsx                          |  26 -
 src/mobile/MobileMenu.scss                         | 271 -------
 webpack.config.js                                  |   6 +-
 49 files changed, 382 insertions(+), 2331 deletions(-)
 delete mode 100644 deploy/mobile/image.html
 delete mode 100644 deploy/mobile/ink.html
 create mode 100644 src/client/util/type_decls.txt
 delete mode 100644 src/mobile/ImageUpload.scss
 delete mode 100644 src/mobile/ImageUpload.tsx
 delete mode 100644 src/mobile/InkControls.tsx
 delete mode 100644 src/mobile/MobileInkOverlay.scss
 delete mode 100644 src/mobile/MobileInkOverlay.tsx
 delete mode 100644 src/mobile/MobileInterface.scss
 delete mode 100644 src/mobile/MobileInterface.tsx
 delete mode 100644 src/mobile/MobileMain.tsx
 delete mode 100644 src/mobile/MobileMenu.scss

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

diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html
deleted file mode 100644
index cca6f763b..000000000
--- a/deploy/mobile/image.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-
-<head>
-    <title>Dash Mobile</title>
-    <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
-        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
-</head>
-
-<body>
-    <div id="root"></div>
-    <script src="../mobileInterface.js"></script>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/deploy/mobile/ink.html b/deploy/mobile/ink.html
deleted file mode 100644
index 938d85794..000000000
--- a/deploy/mobile/ink.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<html>
-
-<head>
-    <title>Test view</title>
-    <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
-</head>
-
-<body>
-    <div id="root"></div>
-    <script src="../inkControls.js"></script>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/src/.DS_Store b/src/.DS_Store
index 75cff7b55..7a0b53ce0 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index f9e282993..fc048b155 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -82,10 +82,6 @@ export function returnEmptyFilter() {
     return [] as string[];
 }
 
-export function returnEmptyDoclist() {
-    return [] as any[];
-}
-
 // eslint-disable-next-line @typescript-eslint/no-namespace
 export namespace ClientUtils {
     export const CLICK_TIME = 300;
diff --git a/src/ServerUtils.ts b/src/ServerUtils.ts
index ade4ca35d..7e821cda2 100644
--- a/src/ServerUtils.ts
+++ b/src/ServerUtils.ts
@@ -2,6 +2,7 @@ import { Socket } from 'socket.io';
 import { Message } from './server/Message';
 import { Utils } from './Utils';
 
+// eslint-disable-next-line @typescript-eslint/no-namespace
 export namespace ServerUtils {
     export function Emit<T>(socket: Socket, message: Message<T>, args: T) {
         Utils.log('Emit', message.Name, args, false);
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 133f8f2ce..b9e0943b6 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,7 +1,7 @@
 // export const ts = (window as any).ts;
 // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
 // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
-import * as typescriptlib from '!!raw-loader!./type_decls.d';
+import * as typescriptlib from './type_decls.d';
 import * as ts from 'typescript';
 import { Doc, FieldType } from '../../fields/Doc';
 import { RefField } from '../../fields/RefField';
diff --git a/src/client/util/type_decls.txt b/src/client/util/type_decls.txt
new file mode 100644
index 000000000..1a93bbe59
--- /dev/null
+++ b/src/client/util/type_decls.txt
@@ -0,0 +1,224 @@
+//@ts-ignore
+declare type PropertyKey = string | number | symbol;
+interface Array<T> {
+    length: number;
+    toString(): string;
+    toLocaleString(): string;
+    pop(): T | undefined;
+    push(...items: T[]): number;
+    concat(...items: ConcatArray<T>[]): T[];
+    concat(...items: (T | ConcatArray<T>)[]): T[];
+    join(separator?: string): string;
+    reverse(): T[];
+    shift(): T | undefined;
+    slice(start?: number, end?: number): T[];
+    sort(compareFn?: (a: T, b: T) => number): this;
+    splice(start: number, deleteCount?: number): T[];
+    splice(start: number, deleteCount: number, ...items: T[]): T[];
+    unshift(...items: T[]): number;
+    indexOf(searchElement: T, fromIndex?: number): number;
+    lastIndexOf(searchElement: T, fromIndex?: number): number;
+    every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
+    some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
+    forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
+    map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
+    filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
+    filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];
+    reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
+    reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
+    reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
+    reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
+    reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
+    reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
+
+    [n: number]: T;
+}
+
+interface Function {
+    apply(this: Function, thisArg: any, argArray?: any): any;
+    call(this: Function, thisArg: any, ...argArray: any[]): any;
+    bind(this: Function, thisArg: any, ...argArray: any[]): any;
+    toString(): string;
+
+    prototype: any;
+    readonly length: number;
+
+    // Non-standard extensions
+    arguments: any;
+    caller: Function;
+}
+interface Boolean {
+    valueOf(): boolean;
+}
+interface Number {
+    toString(radix?: number): string;
+    toFixed(fractionDigits?: number): string;
+    toExponential(fractionDigits?: number): string;
+    toPrecision(precision?: number): string;
+    valueOf(): number;
+}
+interface IArguments {
+    [index: number]: any;
+    length: number;
+    callee: Function;
+}
+interface RegExp {
+    readonly flags: string;
+    readonly sticky: boolean;
+    readonly unicode: boolean;
+}
+interface Date {
+    now() : string;
+}
+interface String {
+    codePointAt(pos: number): number | undefined;
+    includes(searchString: string, position?: number): boolean;
+    endsWith(searchString: string, endPosition?: number): boolean;
+    normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string;
+    normalize(form?: string): string;
+    repeat(count: number): string;
+    replace(a:any, b:any):string; // bcz: fix this
+    startsWith(searchString: string, position?: number): boolean;
+    anchor(name: string): string;
+    big(): string;
+    blink(): string;
+    bold(): string;
+    fixed(): string;
+    fontcolor(color: string): string;
+    fontsize(size: number): string;
+    fontsize(size: string): string;
+    italics(): string;
+    link(url: string): string;
+    small(): string;
+    strike(): string;
+    sub(): string;
+    sup(): string;
+}
+interface Object {
+    constructor: Function;
+    toString(): string;
+    toLocaleString(): string;
+    valueOf(): Object;
+    hasOwnProperty(v: PropertyKey): boolean;
+    isPrototypeOf(v: Object): boolean;
+    propertyIsEnumerable(v: PropertyKey): boolean;
+}
+interface ConcatArray<T> {
+    readonly length: number;
+    readonly [n: number]: T;
+    join(separator?: string): string;
+    slice(start?: number, end?: number): T[];
+}
+interface URL {
+    hash: string;
+    host: string;
+    hostname: string;
+    href: string;
+    readonly origin: string;
+    password: string;
+    pathname: string;
+    port: string;
+    protocol: string;
+    search: string;
+    username: string;
+    toJSON(): string;
+}
+interface PromiseLike<T> {
+    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>;
+}
+interface Promise<T> {
+    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
+    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
+}
+
+declare const Update: unique symbol;
+declare const Self: unique symbol;
+declare const SelfProxy: unique symbol;
+declare const DataSym: unique symbol;
+declare const HandleUpdate: unique symbol;
+declare const Id: unique symbol;
+declare const OnUpdate: unique symbol;
+declare const Parent: unique symbol;
+declare const Copy: unique symbol;
+declare const ToScriptString: unique symbol;
+
+declare abstract class RefField {
+    readonly [Id]: FieldId;
+
+    constructor();
+}
+
+declare type FieldId = string;
+
+declare abstract class ObjectField {
+    abstract [Copy](): ObjectField;
+}
+
+declare abstract class URLField extends ObjectField {
+    readonly url: URL;
+
+    constructor(url: string);
+    constructor(url: URL);
+}
+
+declare class RichTextField extends URLField { 
+    [Copy](): ObjectField; 
+    constructor(data:string, text: string);
+}
+declare class AudioField extends URLField { [Copy](): ObjectField; }
+declare class VideoField extends URLField { [Copy](): ObjectField; }
+declare class ImageField extends URLField { [Copy](): ObjectField; }
+declare class WebField extends URLField { [Copy](): ObjectField; }
+declare class PdfField extends URLField { [Copy](): ObjectField; }
+
+declare const ComputedField: any;
+declare const CompileScript: any;
+
+// @ts-ignore
+declare type Extract<T, U> = T extends U ? T : never;
+declare type Field = number | string | boolean | ObjectField | RefField;
+declare type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
+declare type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
+
+declare type Opt<T> = T | undefined;
+declare class Doc extends RefField {
+    constructor();
+
+    [key: string]: FieldResult;
+    // [ToScriptString](): string;
+}
+
+declare class List<T extends Field> extends ObjectField {
+    constructor(fields?: T[]);
+    [index: number]: T | (T extends RefField ? Promise<T> : never);
+    [Copy](): ObjectField;
+}
+
+declare class InkField extends ObjectField {
+    constructor(data:Array<{X:number, Y:number}>);
+    [Copy](): ObjectField;
+}
+
+// @ts-ignore
+declare const console: any;
+
+interface DocumentOptions { }
+
+declare const Docs: {
+    ImageDocument(url: string, options?: DocumentOptions): Doc;
+    VideoDocument(url: string, options?: DocumentOptions): Doc;
+    TextDocument(options?: DocumentOptions): Doc;
+    PdfDocument(url: string, options?: DocumentOptions): Doc;
+    WebDocument(url: string, options?: DocumentOptions): Doc;
+    HtmlDocument(html: string, options?: DocumentOptions): Doc;
+    MapDocument(url: string, options?: DocumentOptions): Doc;
+    KVPDocument(document: Doc, options?: DocumentOptions): Doc;
+    FreeformDocument(documents: Doc[], options?: DocumentOptions): Doc;
+    SchemaDocument(columns: string[], documents: Doc[], options?: DocumentOptions): Doc;
+    TreeDocument(documents: Doc[], options?: DocumentOptions): Doc;
+    StackingDocument(documents: Doc[], options?: DocumentOptions): Doc;
+};
+
+declare function idToDoc(id:string):any;
+declare function assignDoc(doc:Doc, field:any, id:any):string;
+declare function d(...args:any[]):any;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index e02e39b8b..23da5a666 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -26,7 +26,7 @@ export interface EditableProps {
     /**
      * The contents to render when not editing
      */
-    contents: string;
+    contents: JSX.Element | string;
     fieldContents?: FieldViewProps;
     fontStyle?: string;
     fontSize?: number;
@@ -301,7 +301,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
                         fontStyle: this._props.fontStyle,
                         fontSize: this._props.fontSize,
                     }}>
-                    {this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this._props.contents ? this._props.contents?.valueOf() : ''}
+                    {this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : (this._props.contents ?? '')}
                 </span>
             </div>
         );
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 804c41091..bcd4d1ee5 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -2,9 +2,9 @@ import * as fitCurve from 'fit-curve';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
+import { returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { Doc, Opt } from '../../fields/Doc';
+import { Doc, Opt, returnEmptyDoclist } from '../../fields/Doc';
 import { InkData, InkField, InkTool } from '../../fields/InkField';
 import { NumCast } from '../../fields/Types';
 import {
@@ -30,6 +30,7 @@ import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { Transform } from '../util/Transform';
 import './GestureOverlay.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
+import { returnEmptyDocViewList } from './StyleProvider';
 import { ActiveFillColor, DocumentView } from './nodes/DocumentView';
 
 export enum ToolglassTools {
@@ -466,7 +467,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
                 isContentActive={returnFalse}
                 renderDepth={0}
                 styleProvider={returnEmptyString}
-                containerViewPath={returnEmptyDoclist}
+                containerViewPath={returnEmptyDocViewList}
                 focus={emptyFunction}
                 whenChildContentsActiveChanged={emptyFunction}
                 childFiltersByRanges={returnEmptyFilter}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index adbe20a63..334983ad0 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -5,22 +5,22 @@ import { Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnTrue } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc';
+import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt, returnEmptyDoclist } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkTool } from '../../fields/InkField';
 import { BoolCast, Cast, NumCast, toList } from '../../fields/Types';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
 import { GestureOverlay } from './GestureOverlay';
 import './LightboxView.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
-import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
+import { OverlayView } from './OverlayView';
+import { DefaultStyleProvider, returnEmptyDocViewList, wavyBorderPath } from './StyleProvider';
 import { DocumentView } from './nodes/DocumentView';
 import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
-import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { OverlayView } from './OverlayView';
 
 interface LightboxViewProps {
     PanelWidth: number;
@@ -290,7 +290,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                             ScreenToLocalTransform={this.lightboxScreenToLocal}
                             renderDepth={0}
                             suppressSetHeight={!!this._doc._layout_fitWidth}
-                            containerViewPath={returnEmptyDoclist}
+                            containerViewPath={returnEmptyDocViewList}
                             childFilters={returnEmptyFilter}
                             childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ac30c5a14..62ad0a3fb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,9 +8,9 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import ResizeObserver from 'resize-observer-polyfill';
 import '../../../node_modules/browndash-components/dist/styles/global.min.css';
-import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils';
+import { ClientUtils, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { DocCast, StrCast, toList } from '../../fields/Types';
@@ -627,7 +627,7 @@ export class MainView extends ObservableReactComponent<object> {
                     Document={this.headerBarDoc}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     styleProvider={DefaultStyleProvider}
                     addDocument={this.addHeaderDoc}
                     removeDocument={this.removeHeaderDoc}
@@ -662,7 +662,7 @@ export class MainView extends ObservableReactComponent<object> {
                     addDocument={undefined}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     styleProvider={this._hideUI ? DefaultStyleProvider : undefined}
                     isContentActive={returnTrue}
                     removeDocument={undefined}
@@ -766,7 +766,7 @@ export class MainView extends ObservableReactComponent<object> {
                         addDocument={undefined}
                         addDocTab={DocumentViewInternal.addDocTabFunc}
                         pinToPres={DocumentView.PinDoc}
-                        containerViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDocViewList}
                         styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -799,7 +799,7 @@ export class MainView extends ObservableReactComponent<object> {
                     PanelWidth={this.leftMenuWidth}
                     PanelHeight={this.leftMenuHeight}
                     renderDepth={0}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     focus={emptyFunction}
                     styleProvider={DefaultStyleProvider}
                     isContentActive={returnTrue}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7bf10467e..5e9677b45 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,9 +4,9 @@ import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
 import ResizeObserver from 'resize-observer-polyfill';
-import { returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
+import { returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
 import { Utils, emptyFunction } from '../../Utils';
-import { Doc } from '../../fields/Doc';
+import { Doc, returnEmptyDoclist } from '../../fields/Doc';
 import { Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { NumCast, toList } from '../../fields/Types';
@@ -16,7 +16,7 @@ import { dropActionType } from '../util/DropActionTypes';
 import { Transform } from '../util/Transform';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import './OverlayView.scss';
-import { DefaultStyleProvider } from './StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from './StyleProvider';
 import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
 
 export type OverlayDisposer = () => void;
@@ -226,7 +226,7 @@ export class OverlayView extends ObservableReactComponent<object> {
                         whenChildContentsActiveChanged={emptyFunction}
                         focus={emptyFunction}
                         styleProvider={DefaultStyleProvider}
-                        containerViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDocViewList}
                         addDocTab={DocumentViewInternal.addDocTabFunc}
                         pinToPres={emptyFunction}
                         childFilters={returnEmptyFilter}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5952d6641..daa8e1720 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -10,9 +10,9 @@ import * as React from 'react';
 import { ColorResult, SketchPicker } from 'react-color';
 import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
 import ResizeObserver from 'resize-observer-polyfill';
-import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc';
 import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
@@ -36,7 +36,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import { PropertiesSection } from './PropertiesSection';
 import './PropertiesView.scss';
-import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider';
+import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener, returnEmptyDocViewList } from './StyleProvider';
 import { DocumentView } from './nodes/DocumentView';
 import { StyleProviderFuncType } from './nodes/FieldView';
 import { OpenWhere } from './nodes/OpenWhere';
@@ -322,7 +322,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         renderDepth={1}
                         fitContentsToBox={returnTrue}
                         styleProvider={DefaultStyleProvider}
-                        containerViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDocViewList}
                         dontCenter="y"
                         isDocumentActive={returnFalse}
                         isContentActive={emptyFunction}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index e0d0de165..680c8ed0e 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,8 +1,8 @@
 import { computed, ObservableSet, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../ClientUtils';
-import { Doc, DocListCast } from '../../fields/Doc';
+import { returnEmptyFilter, returnFalse, returnTrue } from '../../ClientUtils';
+import { Doc, DocListCast, returnEmptyDoclist } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, StrCast } from '../../fields/Types';
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index f1b5e4db2..dab1298d5 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -6,9 +6,9 @@ import { Toggle, ToggleType, Type } from 'browndash-components';
 import { Lambda, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 08f3f3d65..486c826b6 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -4,8 +4,8 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../ClientUtils';
+import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 03ade6579..0f12c111e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import * as CSS from 'csstype';
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, DivHeight, returnEmptyDoclist, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
+import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
 import { Doc, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -30,6 +30,7 @@ import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { StyleProp } from '../StyleProp';
+import { returnEmptyDocViewList } from '../StyleProvider';
 import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
 import './CollectionStackingView.scss';
 import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
@@ -661,7 +662,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     renderDepth={this._props.renderDepth}
                     focus={emptyFunction}
                     styleProvider={this._props.styleProvider}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     whenChildContentsActiveChanged={emptyFunction}
                     childFilters={this._props.childFilters}
                     childFiltersByRanges={this._props.childFiltersByRanges}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index e2ad5b31d..5ae08e535 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,6 +1,3 @@
-/* eslint-disable jsx-a11y/control-has-associated-label */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -51,7 +48,7 @@ interface CSVFieldColumnProps {
     addDocument: (doc: Doc | Doc[]) => boolean;
     createDropTarget: (ele: HTMLDivElement) => void;
     screenToLocalTransform: () => Transform;
-    refList: any[];
+    refList: HTMLElement[];
 }
 
 @observer
@@ -64,7 +61,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     @observable _heading = '';
     @observable _color = '';
 
-    constructor(props: any) {
+    constructor(props: CSVFieldColumnProps) {
         super(props);
         makeObservable(this);
         this._heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading;
@@ -118,7 +115,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
         this._props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this._props.pivotField, drop.val, false));
         return true;
     });
-    getValue = (value: string): any => {
+    getValue = (value: string) => {
         const parsed = parseInt(value);
         if (!isNaN(parsed)) return parsed;
         if (value.toLowerCase().indexOf('true') > -1) return true;
@@ -212,7 +209,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                 <div className="colorOptions">
                     {colors.map(col => {
                         const palette = PastelSchemaPalette.get(col);
-                        return <div className={'colorPicker' + (selected === palette ? ' active' : '')} style={{ backgroundColor: palette }} onClick={() => this.changeColumnColor(palette!)} />;
+                        return <div key={col} className={'colorPicker' + (selected === palette ? ' active' : '')} style={{ backgroundColor: palette }} onClick={() => this.changeColumnColor(palette!)} />;
                     })}
                 </div>
             </div>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0557a9102..a60cd98ac 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -2,8 +2,8 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import ResizeObserver from 'resize-observer-polyfill';
-import { DivHeight, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
+import { DivHeight, returnAll, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
+import { Doc, DocListCast, Opt, returnEmptyDoclist, StrListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { listSpec } from '../../../fields/Schema';
@@ -25,6 +25,7 @@ import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProp';
+import { returnEmptyDocViewList } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import './CollectionTreeView.scss';
@@ -369,7 +370,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     renderDepth={this._props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this._props.styleProvider}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     whenChildContentsActiveChanged={emptyFunction}
                     childFilters={this._props.childFilters}
                     childFiltersByRanges={this._props.childFiltersByRanges}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ee5c4afc0..f168db81b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -7,9 +7,9 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import ResizeObserver from 'resize-observer-polyfill';
-import { ClientUtils, DashColor, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
+import { ClientUtils, DashColor, lightOrDark, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -157,8 +157,8 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
                     addDocTab={this._props.addDocTab}
                     // eslint-disable-next-line no-use-before-define
                     pinToPres={TabDocView.PinDoc}
-                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDocViewList}
+                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDocViewList}
                     searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     fitContentsToBox={returnTrue}
                     xPadding={this.xPadding}
@@ -617,8 +617,8 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                     PanelWidth={this.PanelWidth}
                     PanelHeight={this.PanelHeight}
                     styleProvider={DefaultStyleProvider}
-                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDocViewList}
+                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDocViewList}
                     searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     addDocument={undefined}
                     removeDocument={this.remDocTab}
@@ -628,7 +628,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                     dontCenter="y"
                     whenChildContentsActiveChanged={this.whenChildContentActiveChanges}
                     focus={this.focusFunc}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     pinToPres={TabDocView.PinDoc}
                 />
                 {this.disableMinimap() ? null : <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 54053d038..c0fe7a894 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -4,9 +4,9 @@ import { IconButton, Size } from 'browndash-components';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
+import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b8509a005..5125bdb6c 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { Button, IconButton } from 'browndash-components';
@@ -14,7 +12,7 @@ import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch, undoable } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionMulticolumnView.scss';
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
@@ -42,7 +40,7 @@ const resizerWidth = 8;
 export class CollectionMulticolumnView extends CollectionSubView() {
     @observable _startIndex = 0;
 
-    constructor(props: any) {
+    constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -305,7 +303,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                 addDocTab={this._props.addDocTab}
                 pinToPres={this._props.pinToPres}
-                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
+                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
             />
         );
     };
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3fe3d5343..2833ff2f8 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -8,7 +8,7 @@ import { dropActionType } from '../../../util/DropActionTypes';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
@@ -33,7 +33,7 @@ const resizerHeight = 8;
 
 @observer
 export class CollectionMultirowView extends CollectionSubView() {
-    constructor(props: any) {
+    constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -284,7 +284,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                 addDocTab={this._props.addDocTab}
                 pinToPres={this._props.pinToPres}
-                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
+                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'}
             />
         );
     };
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 931e2c5e0..10a6fa2e9 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -68,7 +68,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
                 style={{
                     pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
                     width: this.props.width,
-                    backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
+                    backgroundColor: !this.props.isContentActive?.() ? '' : (this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string),
                 }}>
                 <div className="multiColumnResizer-hdl" onPointerDown={e => this.registerResizing(e)} />
             </div>
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index cff0a8b4c..918365700 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -66,7 +66,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
                 style={{
                     pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
                     height: this.props.height,
-                    backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
+                    backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string,
                 }}>
                 <div className="multiRowResizer-hdl" onPointerDown={e => this.registerResizing(e)} />
             </div>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 6bea53355..8b0639b3b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -4,7 +4,7 @@ import { Popup, PopupTrigger, Type } from 'browndash-components';
 import { ObservableMap, action, computed, makeObservable, observable, observe, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils';
+import { returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils';
 import { emptyFunction } from '../../../../Utils';
 import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { DocData } from '../../../../fields/DocSymbols';
@@ -22,12 +22,12 @@ import { ContextMenu } from '../../ContextMenu';
 import { EditableView } from '../../EditableView';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { StyleProp } from '../../StyleProp';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
 import { Colors } from '../../global/globalEnums';
 import { DocumentView } from '../../nodes/DocumentView';
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FocusViewOptions } from '../../nodes/FocusViewOptions';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
@@ -49,14 +49,14 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', '
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
-    private _keysDisposer: any;
+    private _keysDisposer?: () => void;
     private _previewRef: HTMLDivElement | null = null;
     private _makeNewColumn: boolean = false;
     private _documentOptions: DocumentOptions = new DocumentOptions();
     private _tableContentRef: HTMLDivElement | null = null;
     private _menuTarget = React.createRef<HTMLDivElement>();
 
-    constructor(props: any) {
+    constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -76,7 +76,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _columnMenuIndex: number | undefined = undefined;
     @observable _newFieldWarning: string = '';
     @observable _makeNewField: boolean = false;
-    @observable _newFieldDefault: any = 0;
+    @observable _newFieldDefault: boolean | number | string | undefined = 0;
     @observable _newFieldType: ColumnType = ColumnType.Number;
     @observable _menuValue: string = '';
     @observable _filterColumnIndex: number | undefined = undefined;
@@ -161,11 +161,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
         this._keysDisposer = observe(
             this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
-            (change: any) => {
+            change => {
                 switch (change.type) {
                     case 'splice':
                         // prettier-ignore
-                        (change as any).added.forEach((doc: Doc) => // for each document added 
+                        change.added.filter(doc => doc instanceof Doc).map(doc => doc as Doc).forEach((doc: Doc) => // for each document added 
                             Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself)
                                 Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
                                     !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author'))))));
@@ -270,7 +270,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     addRow = (doc: Doc | Doc[]) => this.addDocument(doc);
 
     @undoBatch
-    changeColumnKey = (index: number, newKey: string, defaultVal?: any) => {
+    changeColumnKey = (index: number, newKey: string, defaultVal?: string | number | boolean) => {
         if (!this.documentKeys.includes(newKey)) {
             this.addNewKey(newKey, defaultVal);
         }
@@ -281,7 +281,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @undoBatch
-    addColumn = (key: string, defaultVal?: any) => {
+    addColumn = (key: string, defaultVal?: string | number | boolean) => {
         if (!this.documentKeys.includes(key)) {
             this.addNewKey(key, defaultVal);
         }
@@ -298,7 +298,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    addNewKey = (key: string, defaultVal: any) =>
+    addNewKey = (key: string, defaultVal?: string | number | boolean) =>
         this.childDocs.forEach(doc => {
             doc[DocData][key] = defaultVal;
         });
@@ -317,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    startResize = (e: any, index: number) => {
+    startResize = (e: React.PointerEvent, index: number) => {
         this._displayColumnWidths = this.storedColumnWidths;
         setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction);
     };
@@ -604,7 +604,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     scrollToDoc = (doc: Doc, options: FocusViewOptions) => {
-        const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
+        const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find(node => node.id === doc[Id]);
         if (found) {
             const rect = found.getBoundingClientRect();
             const localRect = this.ScreenToLocalBoxXf().transformBounds(rect.left, rect.top, rect.width, rect.height);
@@ -625,9 +625,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                         type="number"
                         name=""
                         id=""
-                        value={this._newFieldDefault ?? 0}
+                        value={Number(this._newFieldDefault ?? 0)}
                         onPointerDown={e => e.stopPropagation()}
-                        onChange={action((e: any) => {
+                        onChange={action(e => {
                             this._newFieldDefault = e.target.value;
                         })}
                     />
@@ -637,11 +637,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                     <>
                         <input
                             type="checkbox"
-                            name=""
-                            id=""
-                            value={this._newFieldDefault}
+                            value={this._newFieldDefault?.toString()}
                             onPointerDown={e => e.stopPropagation()}
-                            onChange={action((e: any) => {
+                            onChange={action(e => {
                                 this._newFieldDefault = e.target.checked;
                             })}
                         />
@@ -654,9 +652,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                         type="text"
                         name=""
                         id=""
-                        value={this._newFieldDefault ?? ''}
+                        value={this._newFieldDefault?.toString() ?? ''}
                         onPointerDown={e => e.stopPropagation()}
-                        onChange={action((e: any) => {
+                        onChange={action(e => {
                             this._newFieldDefault = e.target.value;
                         })}
                     />
@@ -683,7 +681,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    setKey = (key: string, defaultVal?: any) => {
+    setKey = (key: string, defaultVal?: string | number | boolean) => {
         if (this._makeNewColumn) {
             this.addColumn(key, defaultVal);
         } else {
@@ -856,16 +854,16 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     onKeysPassiveWheel = (e: WheelEvent) => {
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
-        if (!this._oldKeysWheel.scrollTop && e.deltaY <= 0) e.preventDefault();
+        if (!this._oldKeysWheel?.scrollTop && e.deltaY <= 0) e.preventDefault();
         e.stopPropagation();
     };
-    _oldKeysWheel: any;
+    _oldKeysWheel: HTMLDivElement | null = null;
     @computed get keysDropdown() {
         return (
             <div className="schema-key-search">
                 <div
                     className="schema-column-menu-button"
-                    onPointerDown={action((e: any) => {
+                    onPointerDown={action(e => {
                         e.stopPropagation();
                         this._makeNewField = true;
                     })}>
@@ -880,6 +878,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     }}>
                     {this._menuKeys.map(key => (
                         <div
+                            key={key}
                             className="schema-search-result"
                             onPointerDown={e => {
                                 e.stopPropagation();
@@ -962,7 +961,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 {this.renderFilterOptions}
                 <div
                     className="schema-column-menu-button"
-                    onPointerDown={action((e: any) => {
+                    onPointerDown={action(e => {
                         e.stopPropagation();
                         this.closeFilterMenu();
                     })}>
@@ -1013,7 +1012,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0);
     previewWidthFunc = () => this.previewWidth;
     onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
-    _oldWheel: any;
+    _oldWheel: HTMLDivElement | null = null;
     render() {
         return (
             <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)} onPointerMove={e => this.onPointerMove(e)}>
@@ -1112,7 +1111,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 childFiltersByRanges={this.childDocRangeFilters}
                                 searchFilterDocs={this.searchFilterDocs}
                                 styleProvider={DefaultStyleProvider}
-                                containerViewPath={returnEmptyDoclist}
+                                containerViewPath={returnEmptyDocViewList}
                                 moveDocument={this._props.moveDocument}
                                 addDocument={this.addRow}
                                 removeDocument={this._props.removeDocument}
@@ -1137,7 +1136,7 @@ interface CollectionSchemaViewDocProps {
 
 @observer
 class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaViewDocProps> {
-    constructor(props: any) {
+    constructor(props: CollectionSchemaViewDocProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 6b5a34ec0..e0ed8d01e 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -18,8 +18,8 @@ export interface SchemaColumnHeaderProps {
     setSort: (field: string | undefined, desc?: boolean) => void;
     removeColumn: (index: number) => void;
     rowHeight: () => number;
-    resizeColumn: (e: any, index: number) => void;
-    dragColumn: (e: any, index: number) => boolean;
+    resizeColumn: (e: React.PointerEvent, index: number) => void;
+    dragColumn: (e: PointerEvent, index: number) => boolean;
     openContextMenu: (x: number, y: number, index: number) => void;
     setColRef: (index: number, ref: HTMLDivElement) => void;
 }
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 760089ffb..a7e0e916b 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -58,8 +58,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
     selectCell = (doc: Doc, col: number, shift: boolean, ctrl: boolean) => this.schemaView?.selectCell(doc, col, shift, ctrl);
     deselectCell = () => this.schemaView?.deselectAllCells();
     selectedCells = () => this.schemaView?._selectedDocs;
-    setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false;
-    setSelectedColumnValues = (field: any, value: any) => this.schemaView?.setSelectedColumnValues(field, value) ?? false;
+    setColumnValues = (field: string, value: string) => this.schemaView?.setColumnValues(field, value) ?? false;
+    setSelectedColumnValues = (field: string, value: string) => this.schemaView?.setSelectedColumnValues(field, value) ?? false;
     columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth);
     render() {
         return (
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 5874364e0..22506cac1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable jsx-a11y/alt-text */
 /* eslint-disable react/jsx-props-no-spreading */
 /* eslint-disable no-use-before-define */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -10,10 +9,10 @@ import * as React from 'react';
 import DatePicker from 'react-datepicker';
 import 'react-datepicker/dist/react-datepicker.css';
 import Select from 'react-select';
-import { ClientUtils, StopEvent, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils';
+import { ClientUtils, StopEvent, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils';
 import { emptyFunction } from '../../../../Utils';
 import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast, Field } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, returnEmptyDoclist } from '../../../../fields/Doc';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { ColumnType } from '../../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types';
@@ -22,7 +21,7 @@ import { FInfo, FInfoFieldType } from '../../../documents/Documents';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
-import { undoBatch, undoable } from '../../../util/UndoManager';
+import { undoable } from '../../../util/UndoManager';
 import { EditableView } from '../../EditableView';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
@@ -138,7 +137,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                     ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
                     oneLine={this._props.oneLine}
                     allowCRs={this._props.allowCRs}
-                    contents={undefined}
+                    contents={''}
                     fieldContents={fieldProps}
                     editing={selectedCell(this._props) ? undefined : false}
                     GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)}
@@ -209,7 +208,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
 // mj: most of this is adapted from old schema code so I'm not sure what it does tbh
 @observer
 export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
@@ -276,7 +275,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
 
 @observer
 export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
@@ -324,7 +323,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
 }
 @observer
 export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
@@ -343,7 +342,7 @@ export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps
 }
 @observer
 export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
@@ -356,18 +355,19 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
                     style={{ marginRight: 4 }}
                     type="checkbox"
                     checked={BoolCast(this._props.Document[this._props.fieldKey])}
-                    onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
-                        if ((value?.nativeEvent as any).shiftKey) {
+                    onChange={undoable((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
+                        if ((value?.nativeEvent as MouseEvent | PointerEvent).shiftKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
                         } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
-                    })}
+                    }, 'set bool cell')}
                 />
+
                 <EditableView
-                    contents={undefined}
+                    contents=""
                     fieldContents={fieldProps}
                     editing={selectedCell(this._props) ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
-                    SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
+                    SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
                             this._props.finishEdit?.();
@@ -376,7 +376,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
                         const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
                         this._props.finishEdit?.();
                         return set;
-                    })}
+                    }, 'set bool cell')}
                 />
             </div>
         );
@@ -384,7 +384,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
 }
 @observer
 export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 76a8396ff..9ce5f8fc9 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -1,9 +1,9 @@
 /* eslint-disable react/require-default-props */
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
+import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, returnEmptyDoclist } from '../../../fields/Doc';
 import { Transform } from '../../util/Transform';
 import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
 import { SearchBox } from '../search/SearchBox';
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index c86ddb745..b060fc0b6 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -1,17 +1,15 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../ClientUtils';
+import { returnEmptyFilter, returnTrue } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { CreateLinkToActiveAudio, Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { GestureOverlay } from '../GestureOverlay';
-import { DefaultStyleProvider } from '../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
 import { DocumentView } from '../nodes/DocumentView';
 import { OpenWhere } from '../nodes/OpenWhere';
 import { ExploreView } from './ExploreView';
@@ -68,7 +66,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
     @action
     public static SetCookie(cookie: string) {
         if (this.LightboxDoc && cookie) {
-            this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
+            this._docFilters = (f => (this._docFilters ? ([this._docFilters.push(f) as unknown, this._docFilters][1] as string[]) : [f]))(`cookies:${cookie}:provide`);
         }
     }
     public static AddDocTab = (docsIn: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => {
@@ -264,7 +262,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
                     styleProvider={DefaultStyleProvider}
                     ScreenToLocalTransform={this.newLightboxScreenToLocal}
                     renderDepth={0}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     childFilters={this.docFilters}
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
@@ -320,7 +318,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
                             </div>
                         )}
                     </div>
-                    <RecommendationList keywords={NewLightboxView.Keywords} />
+                    <RecommendationList /* keywords={NewLightboxView.Keywords} */ />
                 </div>
             </div>
         );
diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
index dc3339cd3..27413bac3 100644
--- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
+++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
@@ -1,6 +1,4 @@
 /* eslint-disable react/jsx-props-no-spreading */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 /* eslint-disable guard-for-in */
 import { IconButton, Size, Type } from 'browndash-components';
 import * as React from 'react';
@@ -168,7 +166,8 @@ export function RecommendationList() {
                     <div className="keywords">
                         {keywordsLoc &&
                             keywordsLoc.map((word, ind) => (
-                                <div className="keyword">
+                                <div className="keyword" key={word}>
+                                    {' '}
                                     {word}
                                     <IconButton
                                         type={Type.PRIM}
@@ -207,7 +206,7 @@ export function RecommendationList() {
                     </div>
                 )}
             </div>
-            <div className="recommendations">{recs && recs.map((rec: IRecommendation) => <Recommendation {...rec} />)}</div>
+            <div className="recommendations">{recs && recs.map(rec => <Recommendation key={rec.data} {...rec} />)}</div>
         </div>
     );
 }
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 8e74e1ca2..85aff04c3 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -2,9 +2,9 @@ import { Tooltip } from '@mui/material';
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../ClientUtils';
+import { returnEmptyFilter, returnFalse, returnZero } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, Field } from '../../../fields/Doc';
+import { Doc, Field, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocCast } from '../../../fields/Types';
 import { DocumentOptions, FInfo } from '../../documents/Documents';
 import { Transform } from '../../util/Transform';
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index d33d12603..e39caecb6 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,10 +1,11 @@
 import { Property } from 'csstype';
-import { action, computed, makeObservable, trace } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as textfit from 'textfit';
 import { Field, FieldType } from '../../../fields/Doc';
 import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
@@ -100,7 +101,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return textfitParams;
     };
     render() {
-        trace();
+        TraceMobx();
         const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
         const label = this.Title.startsWith('#') ? null : this.Title;
         return (
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 10ff86a38..5026f52fb 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -4,9 +4,9 @@ import { action, computed, makeObservable, observable, runInAction } from 'mobx'
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import wiki from 'wikijs';
-import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils';
+import { returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
 import { DocServer } from '../../DocServer';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -17,6 +17,7 @@ import { SearchUtil } from '../../util/SearchUtil';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { ObservableReactComponent } from '../ObservableReactComponent';
+import { returnEmptyDocViewList } from '../StyleProvider';
 import { DocumentView } from './DocumentView';
 import { StyleProviderFuncType } from './FieldView';
 import './LinkDocPreview.scss';
@@ -286,7 +287,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
                             styleProvider={this._props.styleProvider}
-                            containerViewPath={returnEmptyDoclist}
+                            containerViewPath={returnEmptyDocViewList}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
                             isContentActive={returnFalse}
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index bfd40692b..a4557196e 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -1,12 +1,13 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
 import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { MapProvider, Map as MapboxMap } from 'react-map-gl';
-import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
 import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, Opt, returnEmptyDoclist } from '../../../../fields/Doc';
 import { DocCss, Highlight } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types';
@@ -363,8 +364,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
     deselectPin = () => {
         if (this.selectedPin) {
             // Removes filter
-            Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
-            Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+            Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove');
+            Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove');
             Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
 
             const temp = this.selectedPin;
@@ -536,8 +537,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
     deleteSelectedPin = undoable(() => {
         if (this.selectedPin) {
             // Removes filter
-            Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
-            Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+            Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove');
+            Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove');
             Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
 
             this.removePushpin(this.selectedPin);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 25adfba23..a76805960 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,11 +1,9 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
+import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
@@ -23,6 +21,7 @@ import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
 import { PinDocView } from '../../PinFuncs';
 import { StyleProp } from '../../StyleProp';
+import { returnEmptyDocViewList } from '../../StyleProvider';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { PresBox } from './PresBox';
@@ -105,7 +104,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     embedHeight = () => this.collapsedHeight + this.expandViewHeight;
     embedWidth = () => this._props.PanelWidth() / 2;
     // prettier-ignore
-    styleProvider = ( doc: Doc | undefined, props: Opt<FieldViewProps>, property: string ): any => 
+    styleProvider = ( doc: Doc | undefined, props: Opt<FieldViewProps>, property: string ) => 
         (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property));
     /**
      * The function that is responsible for rendering a preview or not for this
@@ -123,7 +122,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     hideLinkButton
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={this._props.renderDepth + 1}
-                    containerViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDocViewList}
                     childFilters={this._props.childFilters}
                     childFiltersByRanges={this._props.childFiltersByRanges}
                     searchFilterDocs={this._props.searchFilterDocs}
@@ -144,6 +143,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const childDocs = DocListCast(this.targetDoc.data);
         const groupSlides = childDocs.map((doc: Doc, ind: number) => (
             <div
+                key={doc[Id]}
                 className="presItem-groupSlide"
                 onClick={e => {
                     e.stopPropagation();
@@ -156,7 +156,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <EditableView
                         ref={this._titleRef}
                         editing={undefined}
-                        contents={doc.title}
+                        contents={StrCast(doc.title)}
                         overflow="ellipsis"
                         GetValue={() => StrCast(doc.title)}
                         SetValue={(value: string) => {
@@ -179,7 +179,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
-        const element = e.target as any;
+        const element = e.target as HTMLDivElement;
         e.stopPropagation();
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
@@ -580,7 +580,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         className={`presItem-slide ${isCurrent ? 'active' : ''}${activeItem.runProcess ? ' testingv2' : ''}`}
                         style={{
                             display: 'infline-block',
-                            backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
+                            backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
                             // layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
                             border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined,
                         }}>
@@ -602,7 +602,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }
                                 }}
                                 onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}.  `}</div>
-                            <EditableView ref={this._titleRef} oneLine editing={!isSelected ? false : undefined} contents={activeItem.title} overflow="ellipsis" GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
+                            <EditableView ref={this._titleRef} oneLine editing={!isSelected ? false : undefined} contents={StrCast(activeItem.title)} overflow="ellipsis" GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
                         </div>
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index e558e14e3..a85606bc4 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,8 +5,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Flip } from 'react-awesome-reveal';
 import { FaBug } from 'react-icons/fa';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
+import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
 import { AclAdmin, DashVersion } from '../../../fields/DocSymbols';
 import { StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl } from '../../../fields/util';
@@ -33,11 +33,11 @@ import './TopBar.scss';
  * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard.
  */
 @observer
-export class TopBar extends ObservableReactComponent<{}> {
+export class TopBar extends ObservableReactComponent<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: TopBar;
     @observable private _flipDocumentation = 0;
-    constructor(props: any) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         TopBar.Instance = this;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e6a95fd30..7e7c319bf 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1658,6 +1658,10 @@ export namespace Doc {
     }
 }
 
+export function returnEmptyDoclist() {
+    return [] as Doc[];
+}
+
 export function RTFIsFragment(html: string) {
     return html.indexOf('data-pm-slice') !== -1;
 }
diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss
deleted file mode 100644
index e4156ee8e..000000000
--- a/src/mobile/ImageUpload.scss
+++ /dev/null
@@ -1,139 +0,0 @@
-@import '../client/views/global/globalCssVariables.module.scss';
-
-.imgupload_cont {
-    display: flex;
-    justify-content: center;
-    flex-direction: column;
-    align-items: center;
-    max-width: 400px;
-    min-width: 400px;
-
-    .upload_label {
-        font-weight: 700;
-        color: black;
-        background-color: rgba(0, 0, 0, 0);
-        border: solid 3px black;
-        margin: 10px;
-        font-size: 30;
-        height: 70px;
-        width: 80%;
-        display: flex;
-        font-family: sans-serif;
-        text-transform: uppercase;
-        justify-content: center;
-        flex-direction: column;
-        border-radius: 10px;
-    }
-
-    .file {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        direction: ltr;
-    }
-
-    .button_file {
-        text-align: center;
-        height: 50%;
-        width: 50%;
-        background-color: paleturquoise;
-        color: grey;
-        font-size: 3em;
-    }
-
-    .inputfile {
-        width: 0.1px;
-        height: 0.1px;
-        opacity: 0;
-        overflow: hidden;
-        position: absolute;
-        z-index: -1;
-    }
-
-    .inputfile + label {
-        font-weight: 700;
-        color: black;
-        background-color: rgba(0, 0, 0, 0);
-        border: solid 3px black;
-        margin: 10px;
-        font-size: 30;
-        height: 70px;
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        margin-top: 30px;
-        width: 80%;
-        display: flex;
-        font-family: sans-serif;
-        text-transform: uppercase;
-        justify-content: center;
-        flex-direction: column;
-        border-radius: 10px;
-    }
-
-    .inputfile.active + label {
-        font-style: italic;
-        color: black;
-        background-color: lightgreen;
-        border: solid 3px darkgreen;
-    }
-
-    .status {
-        font-size: 2em;
-    }
-}
-
-.image-upload {
-    top: 100%;
-    opacity: 0;
-}
-
-.image-upload.active {
-    top: 0;
-    position: absolute;
-    z-index: 999;
-    height: 100vh;
-    width: 100vw;
-    opacity: 1;
-}
-
-.uploadContainer {
-    top: 40;
-    position: absolute;
-    z-index: 1000;
-    height: 20vh;
-    width: 80vw;
-    opacity: 1;
-}
-
-.closeUpload {
-    position: absolute;
-    border-radius: 10px;
-    top: 3;
-    color: black;
-    font-size: 30;
-    right: 3;
-    z-index: 1002;
-    padding: 0px 3px;
-    background: rgba(0, 0, 0, 0);
-    transition: 0.5s ease all;
-    border: 0px solid;
-}
-
-.loadingImage {
-    display: inline-flex;
-    width: max-content;
-}
-
-.loadingSlab {
-    position: relative;
-    width: 30px;
-    height: 30px;
-    margin: 10;
-    border-radius: 20px;
-    opacity: 0.2;
-    background-color: black;
-    transition:
-        all 2s,
-        opacity 1.5s;
-}
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
deleted file mode 100644
index 36c0d6a4d..000000000
--- a/src/mobile/ImageUpload.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import * as rp from 'request-promise';
-import { ClientUtils } from '../ClientUtils';
-import { DocServer } from '../client/DocServer';
-import { Networking } from '../client/Network';
-import { Docs } from '../client/documents/Documents';
-import { MainViewModal } from '../client/views/MainViewModal';
-import { Doc, Opt } from '../fields/Doc';
-import { List } from '../fields/List';
-import { listSpec } from '../fields/Schema';
-import { Cast } from '../fields/Types';
-import './ImageUpload.scss';
-
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const { DFLT_IMAGE_NATIVE_DIM } = require('../client/views/global/globalCssVariables.module.scss'); // prettier-ignore
-
-export interface ImageUploadProps {
-    Document: Doc; // Target document for upload (upload location)
-}
-
-const inputRef = React.createRef<HTMLInputElement>();
-const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
-
-@observer
-export class Uploader extends React.Component<ImageUploadProps> {
-    @observable nm: string = 'Choose files'; // Text of 'Choose Files' button
-    @observable process: string = ''; // Current status of upload
-    @observable private dialogueBoxOpacity = 1;
-
-    onClick = async () => {
-        try {
-            // eslint-disable-next-line react/destructuring-assignment
-            const col = this.props.Document;
-            await Docs.Prototypes.initialize();
-            const imgPrev = document.getElementById('img_preview');
-            this.setOpacity(1, '1'); // Slab 1
-            if (imgPrev && inputRef.current) {
-                const { files } = inputRef.current;
-                this.setOpacity(2, '1'); // Slab 2
-                if (files && files.length !== 0) {
-                    this.process = 'Uploading Files';
-                    for (let index = 0; index < files.length; ++index) {
-                        const file = files[index];
-                        // eslint-disable-next-line no-await-in-loop
-                        const res = await Networking.UploadFilesToServer({ file });
-                        this.setOpacity(3, '1'); // Slab 3
-                        // For each item that the user has selected
-                        res.map(async ({ result }) => {
-                            const { name } = file;
-                            if (result instanceof Error) {
-                                return;
-                            }
-                            const path = result.accessPaths.agnostic.client;
-                            let doc = null;
-                            // Case 1: File is a video
-                            if (file.type === 'video/mp4') {
-                                doc = Docs.Create.VideoDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
-                                // Case 2: File is a PDF document
-                            } else if (file.type === 'application/pdf') {
-                                doc = Docs.Create.PdfDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
-                                // Case 3: File is another document type (most likely Image)
-                            } else {
-                                doc = Docs.Create.ImageDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
-                            }
-                            this.setOpacity(4, '1'); // Slab 4
-                            const docidsRes = await rp.get(ClientUtils.prepend('/getUserDocumentIds'));
-                            if (!docidsRes) {
-                                throw new Error('No user id returned');
-                            }
-                            const field = await DocServer.GetRefField(JSON.parse(docidsRes).userDocumentId);
-                            let pending: Opt<Doc>;
-                            if (field instanceof Doc) {
-                                pending = col;
-                            }
-                            if (pending) {
-                                const data = Cast(pending.data, listSpec(Doc));
-                                if (data) data.push(doc);
-                                else pending.data = new List([doc]);
-                                this.setOpacity(5, '1'); // Slab 5
-                                this.process = 'File ' + (index + 1).toString() + ' Uploaded';
-                                this.setOpacity(6, '1'); // Slab 6
-                            }
-                            if (index + 1 === files.length) {
-                                this.process = 'Uploads Completed';
-                                this.setOpacity(7, '1'); // Slab 7
-                            }
-                        });
-                    }
-                    // Case in which the user pressed upload and no files were selected
-                } else {
-                    this.process = 'No file selected';
-                }
-                // Three seconds after upload the menu will reset
-                setTimeout(this.clearUpload, 3000);
-            }
-        } catch (error) {
-            console.log(JSON.stringify(error));
-        }
-    };
-
-    // Returns the upload interface for mobile
-    private get uploadInterface() {
-        return (
-            <div className="imgupload_cont">
-                <div className="closeUpload" onClick={() => this.closeUpload()}>
-                    <FontAwesomeIcon icon="window-close" size="lg" />
-                </div>
-                <FontAwesomeIcon icon="upload" size="lg" style={{ fontSize: '130' }} />
-                <input type="file" accept="application/pdf, video/*,image/*" className={`inputFile ${this.nm !== 'Choose files' ? 'active' : ''}`} id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple />
-                <label className="file" id="label" htmlFor="input_image_file">
-                    {this.nm}
-                </label>
-                <div className="upload_label" onClick={this.onClick}>
-                    Upload
-                </div>
-                <img id="img_preview" src="" alt="" />
-                <div className="loadingImage">
-                    <div className="loadingSlab" id="slab1" />
-                    <div className="loadingSlab" id="slab2" />
-                    <div className="loadingSlab" id="slab3" />
-                    <div className="loadingSlab" id="slab4" />
-                    <div className="loadingSlab" id="slab5" />
-                    <div className="loadingSlab" id="slab6" />
-                    <div className="loadingSlab" id="slab7" />
-                </div>
-                <p className="status">{this.process}</p>
-            </div>
-        );
-    }
-
-    // Updates label after a files is selected (so user knows a file is uploaded)
-    inputLabel = async () => {
-        const files: FileList | null = await inputRef.current!.files;
-        if (files && files.length === 1) {
-            this.nm = files[0].name;
-        } else if (files && files.length > 1) {
-            this.nm = files.length.toString() + ' files selected';
-        }
-    }; // Loops through load icons, and resets buttons
-    @action
-    clearUpload = () => {
-        for (let i = 1; i < 8; i++) {
-            this.setOpacity(i, '0.2');
-        }
-        this.nm = 'Choose files';
-
-        if (inputRef.current) {
-            inputRef.current.value = '';
-        }
-        this.process = '';
-    };
-
-    // Clears the upload and closes the upload menu
-    closeUpload = () => {
-        this.clearUpload();
-    };
-
-    // Handles the setting of the loading bar
-    setOpacity = (index: number, opacity: string) => {
-        const slab = document.getElementById('slab' + index);
-        if (slab) slab.style.opacity = opacity;
-    };
-
-    render() {
-        return <MainViewModal contents={this.uploadInterface} isDisplayed interactive dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} closeOnExternalClick={this.closeUpload} />;
-    }
-}
diff --git a/src/mobile/InkControls.tsx b/src/mobile/InkControls.tsx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/mobile/MobileInkOverlay.scss b/src/mobile/MobileInkOverlay.scss
deleted file mode 100644
index b9c1fb146..000000000
--- a/src/mobile/MobileInkOverlay.scss
+++ /dev/null
@@ -1,39 +0,0 @@
-.mobileInkOverlay {
-    border: 10px dashed red;
-    background-color: rgba(0, 0, 0, .05);
-}
-
-.mobileInkOverlay-border {
-    // background-color: rgba(0, 255, 0, .4);    
-    position: absolute;
-    pointer-events: auto;
-    cursor: pointer;
-
-    &.top {
-        width: calc(100% + 20px);
-        height: 10px;
-        top: -10px;
-        left: -10px;
-    }
-
-    &.left {
-        width: 10px;
-        height: calc(100% + 20px);
-        top: -10px;
-        left: -10px;
-    }
-
-    &.right {
-        width: 10px;
-        height: calc(100% + 20px);
-        top: -10px;
-        right: -10px;
-    }
-
-    &.bottom {
-        width: calc(100% + 20px);
-        height: 10px;
-        bottom: -10px;
-        left: -10px;
-    }
-}
\ No newline at end of file
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
deleted file mode 100644
index 6babd2f39..000000000
--- a/src/mobile/MobileInkOverlay.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-import { action, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { DocServer } from '../client/DocServer';
-import { DragManager } from '../client/util/DragManager';
-import { Doc } from '../fields/Doc';
-import { Gestures } from '../pen-gestures/GestureTypes';
-import { GestureContent, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from '../server/Message';
-import './MobileInkOverlay.scss';
-
-@observer
-export default class MobileInkOverlay extends React.Component {
-    public static Instance: MobileInkOverlay;
-
-    @observable private _scale: number = 1;
-    @observable private _width: number = 0;
-    @observable private _height: number = 0;
-    @observable private _x: number = -300;
-    @observable private _y: number = -300;
-    @observable private _text: string = '';
-
-    @observable private _offsetX: number = 0;
-    @observable private _offsetY: number = 0;
-    @observable private _isDragging: boolean = false;
-    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-
-    constructor(props: Readonly<{}>) {
-        super(props);
-        MobileInkOverlay.Instance = this;
-    }
-
-    initialSize(mobileWidth: number, mobileHeight: number) {
-        const maxWidth = window.innerWidth - 30;
-        const maxHeight = window.innerHeight - 30; // -30 for padding
-        if (mobileWidth > maxWidth || mobileHeight > maxHeight) {
-            const scale = Math.min(maxWidth / mobileWidth, maxHeight / mobileHeight);
-            return { width: mobileWidth * scale, height: mobileHeight * scale, scale: scale };
-        }
-        return { width: mobileWidth, height: mobileHeight, scale: 1 };
-    }
-
-    @action
-    initMobileInkOverlay(content: MobileInkOverlayContent) {
-        const { width, height, text } = content;
-        const scaledSize = this.initialSize(width ? width : 0, height ? height : 0);
-        this._width = scaledSize.width;
-        this._height = scaledSize.height;
-        this._scale = scaledSize.scale;
-        this._x = 300; // TODO: center on screen
-        this._y = 25; // TODO: center on screen
-        this._text = text ? text : '';
-    }
-
-    @action
-    updatePosition(content: UpdateMobileInkOverlayPositionContent) {
-        const { dx, dy, dsize } = content;
-        if (dx) this._x += dx;
-        if (dy) this._y += dy;
-        // TODO: scale dsize
-    }
-
-    drawStroke = (content: GestureContent) => {
-        // TODO: figure out why strokes drawn in corner of mobile interface dont get inserted
-
-        const { points, bounds } = content;
-        console.log('received points', points, bounds);
-
-        const B = {
-            right: bounds.right * this._scale + this._x,
-            left: bounds.left * this._scale + this._x, // TODO: scale
-            bottom: bounds.bottom * this._scale + this._y,
-            top: bounds.top * this._scale + this._y, // TODO: scale
-            width: bounds.width * this._scale,
-            height: bounds.height * this._scale,
-        };
-
-        const target = document.elementFromPoint(this._x + 10, this._y + 10);
-        target?.dispatchEvent(
-            new CustomEvent<GestureEvent>('dashOnGesture', {
-                bubbles: true,
-                detail: {
-                    points: points,
-                    gesture: Gestures.Stroke,
-                    bounds: B,
-                },
-            })
-        );
-    };
-
-    uploadDocument = async (content: MobileDocumentUploadContent) => {
-        const { docId } = content;
-        const doc = await DocServer.GetRefField(docId);
-
-        if (doc && doc instanceof Doc) {
-            const target = document.elementFromPoint(this._x + 10, this._y + 10);
-            const dragData = new DragManager.DocumentDragData([doc]);
-            const complete = new DragManager.DragCompleteEvent(false, dragData);
-
-            if (target) {
-                console.log('dispatching upload doc!!!!', target, doc);
-                target.dispatchEvent(
-                    new CustomEvent<DragManager.DropEvent>('dashOnDrop', {
-                        bubbles: true,
-                        detail: {
-                            x: this._x,
-                            y: this._y,
-                            complete: complete,
-                            altKey: false,
-                            metaKey: false,
-                            ctrlKey: false,
-                            shiftKey: false,
-                            embedKey: false,
-                        },
-                    })
-                );
-            } else {
-                alert('TARGET IS UNDEFINED');
-            }
-        }
-    };
-
-    @action
-    dragStart = (e: React.PointerEvent) => {
-        document.removeEventListener('pointermove', this.dragging);
-        document.removeEventListener('pointerup', this.dragEnd);
-        document.addEventListener('pointermove', this.dragging);
-        document.addEventListener('pointerup', this.dragEnd);
-
-        this._isDragging = true;
-        this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left;
-        this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top;
-
-        e.preventDefault();
-        e.stopPropagation();
-    };
-
-    @action
-    dragging = (e: PointerEvent) => {
-        const x = e.pageX - this._offsetX;
-        const y = e.pageY - this._offsetY;
-
-        // TODO: don't allow drag over library?
-        this._x = Math.min(Math.max(x, 0), window.innerWidth - this._width);
-        this._y = Math.min(Math.max(y, 0), window.innerHeight - this._height);
-
-        e.preventDefault();
-        e.stopPropagation();
-    };
-
-    @action
-    dragEnd = (e: PointerEvent) => {
-        document.removeEventListener('pointermove', this.dragging);
-        document.removeEventListener('pointerup', this.dragEnd);
-
-        this._isDragging = false;
-
-        e.preventDefault();
-        e.stopPropagation();
-    };
-
-    render() {
-        return (
-            <div
-                className="mobileInkOverlay"
-                style={{
-                    width: this._width,
-                    height: this._height,
-                    position: 'absolute',
-                    transform: `translate(${this._x}px, ${this._y}px)`,
-                    zIndex: 30000,
-                    pointerEvents: 'none',
-                    borderStyle: this._isDragging ? 'solid' : 'dashed',
-                }}
-                ref={this._mainCont}>
-                <p>{this._text}</p>
-                <div className="mobileInkOverlay-border top" onPointerDown={this.dragStart}></div>
-                <div className="mobileInkOverlay-border bottom" onPointerDown={this.dragStart}></div>
-                <div className="mobileInkOverlay-border left" onPointerDown={this.dragStart}></div>
-                <div className="mobileInkOverlay-border right" onPointerDown={this.dragStart}></div>
-            </div>
-        );
-    }
-}
diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss
deleted file mode 100644
index 4b32c3da0..000000000
--- a/src/mobile/MobileInterface.scss
+++ /dev/null
@@ -1,445 +0,0 @@
-$navbar-height: 120px;
-$pathbar-height: 50px;
-
-@media only screen and (max-device-width: 480px) {
-    * {
-        margin: 0px;
-        padding: 0px;
-        box-sizing: border-box;
-        font-family: sans-serif;
-    }
-}
-
-body {
-    overflow: hidden;
-}
-
-.mobileInterface-container {
-    height: 100%;
-    position: relative;
-    touch-action: none;
-    width: 100%;
-
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-
-// Topbar of Dash Mobile
-.navbar {
-    position: fixed;
-    top: 0px;
-    left: 0px;
-    width: 100vw;
-    height: $navbar-height;
-    background-color: whitesmoke;
-    z-index: 150;
-
-    .cover {
-        position: absolute;
-        right: 0px;
-        top: 0px;
-        height: 120px;
-        width: 120px;
-        background-color: whitesmoke;
-        z-index: 200;
-    }
-
-    .toggle-btn {
-        position: absolute;
-        right: 20px;
-        top: 30px;
-        height: 70px;
-        width: 70px;
-        transition: all 400ms ease-in-out 200ms;
-        z-index: 180;
-    }
-
-    .background {
-        position: absolute;
-        right: 0px;
-        top: 0px;
-        height: 120px;
-        width: 120px;
-        //border: 1px solid black;
-    }
-
-    .background.active {
-        background-color: lightgrey;
-    }
-
-    .toggle-btn-home {
-        right: -200px;
-    }
-
-    .header {
-        position: absolute;
-        top: 50%;
-        top: calc(9px + 50%);
-        right: 50%;
-        transform: translate(50%, -50%);
-        font-size: 40;
-        font-weight: 700;
-        text-align: center;
-        user-select: none;
-        text-transform: uppercase;
-        font-family: Arial, Helvetica, sans-serif;
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        direction: ltr;
-        width: 600px;
-    }
-
-    .toggle-btn span {
-        position: absolute;
-        top: 50%;
-        left: 50%;
-        transform: translate(-50%, -50%);
-        width: 70%;
-        height: 4px;
-        background: black;
-        transition: all 200ms ease;
-        z-index: 180;
-    }
-
-    .toggle-btn span:nth-child(1) {
-        transition: top 200ms ease-in-out;
-        top: 30%;
-    }
-
-    .toggle-btn span:nth-child(3) {
-        transition: top 200ms ease-in-out;
-        top: 70%;
-    }
-
-    .toggle-btn.active {
-        transition: transform 200ms ease-in-out 200ms;
-        transform: rotate(135deg);
-    }
-
-    .toggle-btn.active span:nth-child(1) {
-        top: 50%;
-    }
-
-    .toggle-btn.active span:nth-child(2) {
-        transform: translate(-50%, -50%) rotate(90deg);
-    }
-
-    .toggle-btn.active span:nth-child(3) {
-        top: 50%;
-    }
-}
-
-.sidebar {
-    position: fixed;
-    top: 120px;
-    opacity: 0;
-    right: -100%;
-    width: 80%;
-    height: calc(80% - (120px));
-    z-index: 101;
-    background-color: whitesmoke;
-    transition: all 400ms ease 50ms;
-    padding: 20px;
-    box-shadow: 0 0 5px 5px grey;
-
-    .item {
-        width: 100%;
-        padding: 13px 12px;
-        border-bottom: 1px solid rgba(200, 200, 200, 0.7);
-        font-family: Arial, Helvetica, sans-serif;
-        font-style: normal;
-        font-weight: normal;
-        user-select: none;
-        display: inline-flex;
-        font-size: 35px;
-        text-transform: uppercase;
-        color: black;
-    }
-
-    .ink:focus {
-        outline: 1px solid blue;
-    }
-
-    .sidebarButtons {
-        top: 80px;
-        position: relative;
-    }
-}
-
-
-
-
-
-
-.blanket {
-    position: fixed;
-    top: 120px;
-    opacity: 0.5;
-    right: -100%;
-    width: 100%;
-    height: calc(100% - (120px));
-    z-index: 101;
-    background-color: grey;
-    padding: 20px;
-}
-
-.blanket.active {
-    position: absolute;
-    right: 0%;
-    z-index: 100;
-}
-
-.home {
-    position: absolute;
-    top: 30px;
-    left: 30px;
-    font-size: 60;
-    user-select: none;
-    text-transform: uppercase;
-    font-family: Arial, Helvetica, sans-serif;
-    z-index: 200;
-}
-
-.item-type {
-    display: inline;
-    text-transform: lowercase;
-    margin-left: 20px;
-    font-size: 35px;
-    font-style: italic;
-    color: rgb(28, 28, 28);
-}
-
-.item-title {
-    max-width: 70%;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-}
-
-.right {
-    margin-left: 20px;
-    z-index: 200;
-}
-
-.open {
-    right: 20px;
-    font-size: 35;
-    position: absolute;
-}
-
-.left {
-    width: 100%;
-    height: 100%;
-}
-
-
-
-.sidebar.active {
-    position: absolute;
-    right: 0%;
-    opacity: 1;
-    z-index: 101;
-}
-
-.back {
-    position: absolute;
-    left: 42px;
-    top: 0;
-    background: #1a1a1a;
-    width: 50px;
-    height: 100%;
-    display: flex;
-    justify-content: center;
-    text-align: center;
-    flex-direction: column;
-    align-items: center;
-    border-radius: 10px;
-    font-size: 25px;
-    user-select: none;
-    z-index: 100;
-}
-
-.pathbar {
-    position: fixed;
-    top: 118px;
-    left: 0px;
-    background: #1a1a1a;
-    z-index: 120;
-    border-radius: 0px;
-    width: 100%;
-    height: 80px;
-    overflow: hidden;
-
-    .pathname {
-        position: relative;
-        font-size: 25;
-        top: 50%;
-        width: 86%;
-        left: 12%;
-        color: whitesmoke;
-        transform: translate(0%, -50%);
-        z-index: 20;
-        font-family: sans-serif;
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        direction: rtl;
-        text-align: left;
-        text-transform: uppercase;
-    }
-
-    .scrollmenu {
-        overflow: auto;
-        width: 100%;
-        height: 100%;
-        white-space: nowrap;
-        display: inline-flex;
-    }
-
-    .hidePath {
-        position: absolute;
-        height: 100%;
-        width: 200px;
-        left: 0px;
-        top: 0px;
-        background-image: linear-gradient(to right, #1a1a1a, rgba(0, 0, 0, 0));
-        text-align: center;
-        user-select: none;
-        z-index: 99;
-        pointer-events: none;
-    }
-
-    .pathbarItem {
-        position: relative;
-        display: flex;
-        align-items: center;
-        color: whitesmoke;
-        text-align: center;
-        justify-content: center;
-        user-select: none;
-        transform: translate(100px, 0px);
-        font-size: 30px;
-        padding: 10px;
-        text-transform: uppercase;
-
-        .pathbarText {
-            font-family: sans-serif;
-            text-align: center;
-            height: 50px;
-            padding: 10px;
-            font-size: 30px;
-            border-radius: 10px;
-            text-transform: uppercase;
-            margin-left: 20px;
-            position: relative;
-        }
-
-        .pathIcon {
-            transform: translate(0px, 0px);
-            position: relative;
-        }
-    }
-}
-
-
-/**
-* docButton appears at the bottom of mobile document
-* Buttons include: pin to presentation, download, upload, reload
-*/
-.docButton {
-    position: relative;
-    width: 100px;
-    display: flex;
-    height: 100px;
-    font-size: 70px;
-    text-align: center;
-    border: 3px solid black;
-    margin: 20px;
-    z-index: 100;
-    border-radius: 100%;
-    justify-content: center;
-    flex-direction: column;
-    align-items: center;
-}
-
-.docButtonContainer {
-    top: 80%;
-    position: absolute;
-    display: flex;
-    transform: translate(-50%, 0);
-    left: 50%;
-    z-index: 100;
-}
-
-.toolbar {
-    left: 50%;
-    transform: translate(-50%);
-    position: absolute;
-    height: max-content;
-    top: 0px;
-    border-radius: 20px;
-    background-color: lightgrey;
-    opacity: 0;
-    transition: all 400ms ease 50ms;
-}
-
-.toolbar.active {
-    display: inline-block;
-    width: 300px;
-    padding: 5px;
-    opacity: 1;
-    height: max-content;
-    top: -450px;
-}
-
-.colorSelector {
-    position: absolute;
-    top: 550px;
-    left: 280px;
-    transform: translate(-50%, 0);
-    z-index: 100;
-    display: inline-flex;
-    width: max-content;
-    height: max-content;
-    pointer-events: all;
-    font-size: 80px;
-    user-select: none;
-}
-
-// Menu buttons for toggling between list and icon view
-.homeSwitch {
-    position: fixed;
-    top: 212;
-    right: 36px;
-    display: inline-flex;
-    width: max-content;
-    z-index: 99;
-    height: 70px;
-
-    .list {
-        width: 70px;
-        height: 70px;
-        margin: 5;
-        padding: 10;
-        align-items: center;
-        text-align: center;
-        font-size: 50;
-        border-style: solid;
-        border-width: 3;
-        border-color: black;
-        background: whitesmoke;
-        align-self: center;
-        border-radius: 10px;
-    }
-
-    .list.active {
-        color: darkred;
-        border-color: darkred;
-    }
-}
\ No newline at end of file
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
deleted file mode 100644
index 4f37c45a8..000000000
--- a/src/mobile/MobileInterface.tsx
+++ /dev/null
@@ -1,871 +0,0 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import {
-    faAddressCard,
-    faAlignLeft,
-    faAlignRight,
-    faAngleDoubleLeft,
-    faAngleRight,
-    faArrowDown,
-    faArrowLeft,
-    faArrowRight,
-    faArrowUp,
-    faArrowsAltH,
-    faAsterisk,
-    faBars,
-    faBell,
-    faBolt,
-    faBook,
-    faBrain,
-    faBullseye,
-    faCalculator,
-    faCamera,
-    faCaretDown,
-    faCaretLeft,
-    faCaretRight,
-    faCaretSquareDown,
-    faCaretSquareRight,
-    faCaretUp,
-    faCat,
-    faCheck,
-    faChevronLeft,
-    faChevronRight,
-    faClipboard,
-    faClone,
-    faCloudUploadAlt,
-    faCommentAlt,
-    faCompressArrowsAlt,
-    faCut,
-    faEdit,
-    faEllipsisV,
-    faEraser,
-    faExclamation,
-    faExpand,
-    faExternalLinkAlt,
-    faExternalLinkSquareAlt,
-    faEye,
-    faFileAlt,
-    faFileAudio,
-    faFileDownload,
-    faFilePdf,
-    faFilm,
-    faFilter,
-    faFolderOpen,
-    faFont,
-    faGlobeAsia,
-    faHandPointLeft,
-    faHighlighter,
-    faHome,
-    faImage,
-    faLocationArrow,
-    faLongArrowAltLeft,
-    faLongArrowAltRight,
-    faMicrophone,
-    faCircleHalfStroke,
-    faMinus,
-    faMobile,
-    faMousePointer,
-    faMusic,
-    faObjectGroup,
-    faPaintBrush,
-    faPalette,
-    faPause,
-    faPen,
-    faPenNib,
-    faPhone,
-    faPlay,
-    faPlus,
-    faPortrait,
-    faQuestionCircle,
-    faQuoteLeft,
-    faRedoAlt,
-    faReply,
-    faSearch,
-    faStamp,
-    faStickyNote,
-    faStop,
-    faTasks,
-    faTerminal,
-    faTh,
-    faThLarge,
-    faThumbtack,
-    faTimes,
-    faToggleOn,
-    faTrash,
-    faTrashAlt,
-    faTree,
-    faTv,
-    faUndoAlt,
-    faVideo,
-    faWindowClose,
-    faWindowMaximize,
-    faFile as fileSolid,
-} from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../ClientUtils';
-import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
-import { Docs, DocumentOptions } from '../client/documents/Documents';
-import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { SettingsManager } from '../client/util/SettingsManager';
-import { Transform } from '../client/util/Transform';
-import { UndoManager } from '../client/util/UndoManager';
-import { DashboardView } from '../client/views/DashboardView';
-import { GestureOverlay } from '../client/views/GestureOverlay';
-import { AudioBox } from '../client/views/nodes/AudioBox';
-import { DocumentView } from '../client/views/nodes/DocumentView';
-import { RadialMenu } from '../client/views/nodes/RadialMenu';
-import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu';
-import { Doc, DocListCast } from '../fields/Doc';
-import { InkTool } from '../fields/InkField';
-import { List } from '../fields/List';
-import { ScriptField } from '../fields/ScriptField';
-import { Cast, FieldValue, StrCast } from '../fields/Types';
-import './AudioUpload.scss';
-import { Uploader } from './ImageUpload';
-import './ImageUpload.scss';
-import './MobileInterface.scss';
-import { emptyFunction } from '../Utils';
-
-library.add(
-    ...[
-        faTasks,
-        faReply,
-        faQuoteLeft,
-        faHandPointLeft,
-        faFolderOpen,
-        faAngleDoubleLeft,
-        faExternalLinkSquareAlt,
-        faMobile,
-        faThLarge,
-        faWindowClose,
-        faEdit,
-        faTrashAlt,
-        faPalette,
-        faAngleRight,
-        faBell,
-        faTrash,
-        faCamera,
-        faExpand,
-        faCaretDown,
-        faCaretLeft,
-        faCaretRight,
-        faCaretSquareDown,
-        faCaretSquareRight,
-        faArrowsAltH,
-        faPlus,
-        faMinus,
-        faTerminal,
-        faToggleOn,
-        fileSolid,
-        faExternalLinkAlt,
-        faLocationArrow,
-        faSearch,
-        faFileDownload,
-        faStop,
-        faCalculator,
-        faWindowMaximize,
-        faAddressCard,
-        faQuestionCircle,
-        faArrowLeft,
-        faArrowRight,
-        faArrowDown,
-        faArrowUp,
-        faBolt,
-        faBullseye,
-        faCaretUp,
-        faCat,
-        faCheck,
-        faChevronRight,
-        faClipboard,
-        faClone,
-        faCloudUploadAlt,
-        faCommentAlt,
-        faCompressArrowsAlt,
-        faCut,
-        faEllipsisV,
-        faEraser,
-        faExclamation,
-        faFileAlt,
-        faFileAudio,
-        faFilePdf,
-        faFilm,
-        faFilter,
-        faFont,
-        faGlobeAsia,
-        faHighlighter,
-        faLongArrowAltRight,
-        faMicrophone,
-        faCircleHalfStroke,
-        faMousePointer,
-        faMusic,
-        faObjectGroup,
-        faPause,
-        faPen,
-        faPenNib,
-        faPhone,
-        faPlay,
-        faPortrait,
-        faRedoAlt,
-        faStamp,
-        faStickyNote,
-        faThumbtack,
-        faTree,
-        faTv,
-        faUndoAlt,
-        faBook,
-        faVideo,
-        faAsterisk,
-        faBrain,
-        faImage,
-        faPaintBrush,
-        faTimes,
-        faEye,
-        faHome,
-        faLongArrowAltLeft,
-        faBars,
-        faTh,
-        faChevronLeft,
-        faAlignLeft,
-        faAlignRight,
-    ].map(m => m as any)
-);
-
-@observer
-export class MobileInterface extends React.Component {
-    static Instance: MobileInterface;
-    private _library: Doc;
-    private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
-    @observable private _sidebarActive: boolean = false; //to toggle sidebar display
-    @observable private _imageUploadActive: boolean = false; //to toggle image upload
-    @observable private _audioUploadActive: boolean = false;
-    @observable private _menuListView: boolean = false; //to switch between menu view (list / icon)
-    @observable private _ink: boolean = false; //toggle whether ink is being dispalyed
-    @observable private _homeMenu: boolean = true; // to determine whether currently at home menu
-    @observable private dashboards: Doc | null = null; // currently selected document
-    @observable private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu)
-    @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
-    @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
-
-    @computed private get mainContainer() {
-        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : Doc.GuestMobile;
-    }
-
-    constructor(props: Readonly<{}>) {
-        super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), 'myDashboards'); // to access documents in Dash Web
-        MobileInterface.Instance = this;
-    }
-
-    @action
-    componentDidMount() {
-        // if the home menu is in list view -> adjust the menu toggle appropriately
-        this._menuListView = this._homeDoc._type_collection === 'stacking' ? true : false;
-        Doc.ActiveTool = InkTool.None; // ink should intially be set to none
-        Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
-
-        // remove double click to avoid mobile zoom in
-        document.removeEventListener('dblclick', this.onReactDoubleClick);
-        document.addEventListener('dblclick', this.onReactDoubleClick);
-    }
-
-    @action
-    componentWillUnmount = () => {
-        document.removeEventListener('dblclick', this.onReactDoubleClick);
-    };
-
-    // Prevent zooming in when double tapping the screen
-    onReactDoubleClick = (e: MouseEvent) => {
-        e.stopPropagation();
-    };
-
-    // Switch the mobile view to the given doc
-    @action
-    switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => {
-        if (!Doc.UserDoc()) return;
-        if (this._activeDoc === this._homeDoc) {
-            this._parents.push(this._activeDoc);
-            this._homeMenu = false;
-        }
-        this._activeDoc = doc;
-        Doc.UserDoc().activeMobile = doc;
-        onSwitch?.();
-
-        // Ensures that switching to home is not registed
-        UndoManager.undoStack.length = 0;
-        UndoManager.redoStack.length = 0;
-    };
-
-    // For toggling the hamburger menu
-    @action
-    toggleSidebar = () => {
-        this._sidebarActive = !this._sidebarActive;
-
-        if (this._ink) {
-            this.onSwitchInking();
-        }
-    };
-    /**
-     * Method called when 'Library' button is pressed on the home screen
-     */
-    switchToLibrary = async () => {
-        this.switchCurrentView(this._library);
-        runInAction(() => (this._homeMenu = false));
-        this.toggleSidebar();
-    };
-
-    /**
-     * Back method for navigating through items
-     */
-    @action
-    back = () => {
-        const header = document.getElementById('header') as HTMLElement;
-        const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
-        // Case 1: Parent document is 'dashboards'
-        if (doc === (Cast(this._library, Doc) as Doc)) {
-            this.dashboards = null;
-            this.switchCurrentView(this._library);
-            // Case 2: Parent document is the 'home' menu (root node)
-        } else if (doc === (Cast(this._homeDoc, Doc) as Doc)) {
-            this._homeMenu = true;
-            this._parents = [];
-            this.dashboards = null;
-            this.switchCurrentView(this._homeDoc);
-            // Case 3: Parent document is any document
-        } else if (doc) {
-            this.dashboards = doc;
-            this.switchCurrentView(doc);
-            this._homeMenu = false;
-            header.textContent = String(doc.title);
-        }
-        this._ink = false; // turns ink off
-    };
-
-    /**
-     * Return 'Home", which implies returning to 'Home' menu buttons
-     */
-    @action
-    returnHome = () => {
-        if (!this._homeMenu || this._sidebarActive) {
-            this._homeMenu = true;
-            this._parents = [];
-            this.dashboards = null;
-            this.switchCurrentView(this._homeDoc);
-        }
-        if (this._sidebarActive) {
-            this.toggleSidebar();
-        }
-    };
-
-    /**
-     * Return to primary Dashboard in library (Dashboards Doc)
-     */
-    @action
-    returnMain = () => {
-        this._parents = [this._homeDoc];
-        this.switchCurrentView(this._library);
-        this._homeMenu = false;
-        this.dashboards = null;
-    };
-
-    /**
-     * Note: window.innerWidth and window.screen.width compute different values.
-     * window.screen.width is the display size, however window.innerWidth is the
-     * display resolution which computes differently.
-     */
-    returnWidth = () => window.innerWidth; //The windows width
-    returnHeight = () => window.innerHeight - 300; //Calculating the windows height (-300 to account for topbar)
-    whitebackground = () => 'white';
-    /**
-     * DocumentView for graphic display of all documents
-     */
-    @computed get displayDashboards() {
-        return !this.mainContainer ? null : (
-            <div style={{ position: 'relative', top: '198px', height: `calc(100% - 350px)`, width: '100%', left: '0%' }}>
-                <DocumentView
-                    Document={this.mainContainer}
-                    addDocument={returnFalse}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    removeDocument={undefined}
-                    ScreenToLocalTransform={Transform.Identity}
-                    PanelWidth={this.returnWidth}
-                    PanelHeight={this.returnHeight}
-                    renderDepth={0}
-                    isDocumentActive={returnTrue}
-                    isContentActive={emptyFunction}
-                    focus={emptyFunction}
-                    styleProvider={this.whitebackground}
-                    containerViewPath={returnEmptyDoclist}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    childFilters={returnEmptyFilter}
-                    childFiltersByRanges={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                />
-            </div>
-        );
-    }
-
-    /**
-     * Handles the click functionality in the library panel.
-     * Navigates to the given doc and updates the sidebar.
-     * @param doc: doc for which the method is called
-     */
-    handleClick = async (doc: Doc) => {
-        runInAction(() => {
-            if (doc.type !== 'collection' && this._sidebarActive) {
-                this._parents.push(this._activeDoc);
-                this.switchCurrentView(doc);
-                this._homeMenu = false;
-                this.toggleSidebar();
-            } else {
-                this._parents.push(this._activeDoc);
-                this.switchCurrentView(doc);
-                this._homeMenu = false;
-                this.dashboards = doc;
-            }
-        });
-    };
-
-    /**
-     * Called when an item in the library is clicked and should
-     * be opened (open icon on RHS of all menu items)
-     * @param doc doc to be opened
-     */
-    @action
-    openFromSidebar = (doc: Doc) => {
-        this._parents.push(this._activeDoc);
-        this.switchCurrentView(doc);
-        this._homeMenu = false;
-        this.dashboards = doc;
-        this.toggleSidebar();
-    };
-
-    // Renders the graphical pathbar
-    renderPathbar = () => {
-        const docPath = [...this._parents, this._activeDoc];
-        const items = docPath.map((doc: Doc, index: any) => (
-            <div className="pathbarItem" key={index}>
-                {index === 0 ? null : <FontAwesomeIcon key="icon" className="pathIcon" icon="angle-right" size="lg" />}
-                <div className="pathbarText" style={{ backgroundColor: this._homeMenu || doc === this._activeDoc ? 'rgb(119,17,37)' : undefined }} onClick={() => this.handlePathClick(doc, index)}>
-                    {StrCast(doc.title)}
-                </div>
-            </div>
-        ));
-        return (
-            <div className="pathbar">
-                <div className="scrollmenu">{items}</div>
-                {!this._parents.length ? null : (
-                    <div className="back">
-                        <FontAwesomeIcon onClick={this.back} icon={'chevron-left'} color="white" size={'2x'} />
-                    </div>
-                )}
-                <div className="hidePath" />
-            </div>
-        );
-    };
-
-    // Handles when user clicks on a document in the pathbar
-    @action
-    handlePathClick = async (doc: Doc, index: number) => {
-        const library = await this._library;
-        if (doc === library) {
-            this.dashboards = null;
-            this.switchCurrentView(doc);
-            this._parents.length = index;
-        } else if (doc === this._homeDoc) {
-            this.returnHome();
-        } else {
-            this.dashboards = doc;
-            this.switchCurrentView(doc);
-            this._parents.length = index;
-        }
-    };
-
-    // Renders the contents of the menu and sidebar
-    @computed get renderDefaultContent() {
-        if (this._homeMenu) {
-            return (
-                <div>
-                    <div className="navbar">
-                        <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                        <div className="header" id="header">
-                            {StrCast(this._homeDoc.title)}
-                        </div>
-                        <div className="cover" id="cover" onClick={e => e.stopPropagation()}></div>
-                        <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}>
-                            <span></span>
-                            <span></span>
-                            <span></span>
-                        </div>
-                    </div>
-                    {this.renderPathbar()}
-                </div>
-            );
-        }
-        // stores dashboards documents as 'dashboards' variable
-        let dashboards = Doc.MyDashboards;
-        if (this.dashboards) {
-            dashboards = this.dashboards;
-        }
-        // returns a list of navbar buttons as 'buttons'
-        const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => {
-            if (doc.type !== 'ink') {
-                return (
-                    <div className="item" key={index}>
-                        <div className="item-title" onClick={() => this.handleClick(doc)}>
-                            {' '}
-                            {doc.title as string}{' '}
-                        </div>
-                        <div className="item-type" onClick={() => this.handleClick(doc)}>
-                            {doc.type as string}
-                        </div>
-                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === 'collection' ? 'block' : 'none'}` }} />
-                        <FontAwesomeIcon className="open" onClick={() => this.openFromSidebar(doc)} icon="external-link-alt" size="lg" />
-                    </div>
-                );
-            }
-        });
-
-        return (
-            <div>
-                <div className="navbar">
-                    <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                    <div className="header" id="header">
-                        {this._sidebarActive ? 'library' : (this._activeDoc.title as string)}
-                    </div>
-                    <div className={`toggle-btn ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}>
-                        <span></span>
-                        <span></span>
-                        <span></span>
-                    </div>
-                    <div className={`background ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
-                </div>
-                {this.renderPathbar()}
-                <div className={`sidebar ${this._sidebarActive ? 'active' : ''}`}>
-                    <div className="sidebarButtons">
-                        {this.dashboards ? (
-                            <>
-                                {buttons}
-                                <div className="item" key="home" onClick={this.returnMain} style={{ opacity: 0.7 }}>
-                                    <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
-                                    <div className="item-type">Return to dashboards</div>
-                                </div>
-                            </>
-                        ) : (
-                            <>
-                                {buttons}
-                                <div className="item" style={{ opacity: 0.7 }} onClick={() => this.createNewDashboard()}>
-                                    <FontAwesomeIcon className="right" icon="plus" size="lg" />
-                                    <div className="item-type">Create New Dashboard</div>
-                                </div>
-                            </>
-                        )}
-                    </div>
-                </div>
-                <div className={`blanket ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
-            </div>
-        );
-    }
-
-    /**
-     * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
-     */
-    @action
-    createNewDashboard = (id?: string) => {
-        const scens = Doc.MyDashboards;
-        const dashboardCount = DocListCast(scens.data).length + 1;
-        const freeformOptions: DocumentOptions = {
-            x: 0,
-            y: 400,
-            title: 'Collection ' + dashboardCount,
-        };
-
-        const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
-
-        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
-        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleComic!, cloneDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(['Toggle Comic Mode', 'New Dashboard Layout']);
-
-        Doc.AddDocToList(scens, 'data', dashboardDoc);
-    };
-
-    // Button for switching between pen and ink mode
-    @action
-    onSwitchInking = () => {
-        const button = document.getElementById('inkButton') as HTMLElement;
-        button.style.backgroundColor = this._ink ? 'white' : 'black';
-        button.style.color = this._ink ? 'black' : 'white';
-
-        if (!this._ink) {
-            Doc.ActiveTool = InkTool.Pen;
-            this._ink = true;
-        } else {
-            Doc.ActiveTool = InkTool.None;
-            this._ink = false;
-        }
-    };
-
-    // The static ink menu that appears at the top
-    @computed get inkMenu() {
-        return this._activeDoc._type_collection !== CollectionViewType.Docking || !this._ink ? null : <div className="colorSelector">{/* <CollectionFreeFormViewChrome /> */}</div>;
-    }
-
-    // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
-    @computed get undo() {
-        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
-            return (
-                <div
-                    className="docButton"
-                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanUndo() ? '1' : '0.4' }}
-                    id="undoButton"
-                    title="undo"
-                    onClick={(e: React.MouseEvent) => {
-                        UndoManager.Undo();
-                        e.stopPropagation();
-                    }}>
-                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" />
-                </div>
-            );
-        } else return null;
-    }
-
-    // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
-    @computed get redo() {
-        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
-            return (
-                <div
-                    className="docButton"
-                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanRedo() ? '1' : '0.4' }}
-                    id="undoButton"
-                    title="redo"
-                    onClick={(e: React.MouseEvent) => {
-                        UndoManager.Redo();
-                        e.stopPropagation();
-                    }}>
-                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" />
-                </div>
-            );
-        } else return null;
-    }
-
-    // DocButton for switching into ink mode
-    @computed get drawInk() {
-        return !this.mainContainer || this._activeDoc._type_collection !== CollectionViewType.Docking ? null : (
-            <div className="docButton" id="inkButton" onClick={this.onSwitchInking}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib" />
-            </div>
-        );
-    }
-
-    // DocButton: Button that appears on the bottom of the screen to initiate image upload
-    @computed get uploadImageButton() {
-        if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._type_collection !== CollectionViewType.Docking && this._activeDoc.title !== 'WORKSPACES') {
-            return (
-                <div className="docButton" id="imageButton" onClick={this.toggleUpload}>
-                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
-                </div>
-            );
-        } else return null;
-    }
-
-    // DocButton to download images on the mobile
-    @computed get downloadDocument() {
-        if (this._activeDoc.type === 'image' || this._activeDoc.type === 'pdf' || this._activeDoc.type === 'video') {
-            return (
-                <div className="docButton" title={'Download Image'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => window.open(this._activeDoc['data-path']?.toString())}>
-                    {' '}
-                    {/*  daa-path holds the url */}
-                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
-                </div>
-            );
-        } else return null;
-    }
-
-    // DocButton for pinning images to presentation
-    @computed get pinToPresentation() {
-        // Only making button available if it is an image
-        if (!(this._activeDoc.type === 'collection' || this._activeDoc.type === 'presentation')) {
-            return (
-                <div className="docButton" title={'Pin to presentation'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => DocumentView.PinDoc(this._activeDoc, {})}>
-                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
-                </div>
-            );
-        } else return null;
-    }
-
-    // Buttons for switching the menu between large and small icons
-    @computed get switchMenuView() {
-        return this._activeDoc.title !== this._homeDoc.title ? null : (
-            <div className="homeSwitch">
-                <div className={`list ${!this._menuListView ? 'active' : ''}`} onClick={this.changeToIconView}>
-                    <FontAwesomeIcon size="sm" icon="th-large" />
-                </div>
-                <div className={`list ${this._menuListView ? 'active' : ''}`} onClick={this.changeToListView}>
-                    <FontAwesomeIcon size="sm" icon="bars" />
-                </div>
-            </div>
-        );
-    }
-
-    // Logic for switching the menu into the icons
-    @action
-    changeToIconView = () => {
-        if ((this._homeDoc._type_collection = 'stacking')) {
-            this._menuListView = false;
-            this._homeDoc._type_collection = 'masonry';
-            this._homeDoc.columnWidth = 300;
-            this._homeDoc._columnWidth = 300;
-            const menuButtons = DocListCast(this._homeDoc.data);
-            menuButtons.map(doc => {
-                const buttonData = DocListCast(doc.data);
-                buttonData[1]._nativeWidth = 0.1;
-                buttonData[1]._width = 0.1;
-                buttonData[1]._dimMagnitude = 0;
-                buttonData[1]._opacity = 0;
-                doc._nativeWidth = 400;
-            });
-        }
-    };
-
-    // Logic for switching the menu into the stacking view
-    @action
-    changeToListView = () => {
-        if ((this._homeDoc._type_collection = 'masonry')) {
-            this._homeDoc._type_collection = 'stacking';
-            this._menuListView = true;
-            const menuButtons = DocListCast(this._homeDoc.data);
-            menuButtons.map(doc => {
-                const buttonData = DocListCast(doc.data);
-                buttonData[1]._nativeWidth = 450;
-                buttonData[1]._dimMagnitude = 2;
-                buttonData[1]._opacity = 1;
-                doc._nativeWidth = 900;
-            });
-        }
-    };
-
-    // For setting up the presentation document for the home menu
-    @action
-    setupDefaultPresentation = () => {
-        const presentation = Doc.ActivePresentation;
-
-        if (presentation) {
-            this.switchCurrentView(presentation);
-            this._homeMenu = false;
-        }
-    };
-
-    // For toggling image upload pop up
-    @action
-    toggleUpload = () => (this._imageUploadActive = !this._imageUploadActive);
-
-    // For toggling audio record and dictate pop up
-    @action
-    toggleAudio = () => (this._audioUploadActive = !this._audioUploadActive);
-
-    // Button for toggling the upload pop up in a collection
-    @action
-    toggleUploadInCollection = () => {
-        const button = document.getElementById('imageButton') as HTMLElement;
-        button.style.backgroundColor = this._imageUploadActive ? 'white' : 'black';
-        button.style.color = this._imageUploadActive ? 'black' : 'white';
-
-        this._imageUploadActive = !this._imageUploadActive;
-    };
-
-    // For closing the image upload pop up
-    @action
-    closeUpload = () => {
-        this._imageUploadActive = false;
-    };
-
-    // Returns the image upload pop up
-    @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : (Cast(Doc.SharingDoc(), Doc) as Doc);
-        return <Uploader Document={doc} />;
-    }
-
-    // Radial menu can only be used if it is a colleciton and it is not a homeDoc
-    // (and cannot be used on Dashboard to avoid pin to presentation opening on right)
-    @computed get displayRadialMenu() {
-        return this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc._type_collection !== CollectionViewType.Docking ? <RadialMenu /> : null;
-    }
-
-    onDragOver = (e: React.DragEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-    };
-
-    /**
-     * MENU BUTTON
-     * Switch view from mobile menu to access the mobile uploads
-     * Global function name: openMobileUploads()
-     */
-    @action
-    switchToMobileUploads = () => {
-        const mobileUpload = Cast(Doc.SharingDoc(), Doc) as Doc;
-        this.switchCurrentView(mobileUpload);
-        this._homeMenu = false;
-    };
-
-    render() {
-        return (
-            <div className="mobileInterface-container" onDragOver={this.onDragOver}>
-                <SettingsManager />
-                <div className={`image-upload ${this._imageUploadActive ? 'active' : ''}`}>{this.uploadImage}</div>
-                {this.switchMenuView}
-                {this.inkMenu}
-                <GestureOverlay isActive={true}>
-                    <div style={{ display: 'none' }}>
-                        <RichTextMenu key="rich" />
-                    </div>
-                    <div className="docButtonContainer">
-                        {this.pinToPresentation}
-                        {this.downloadDocument}
-                        {this.undo}
-                        {this.redo}
-                        {this.drawInk}
-                        {this.uploadImageButton}
-                    </div>
-                    {this.displayDashboards}
-                    {this.renderDefaultContent}
-                </GestureOverlay>
-                {this.displayRadialMenu}
-            </div>
-        );
-    }
-}
-
-//Global functions for mobile menu
-ScriptingGlobals.add(function switchToMobileLibrary() {
-    return MobileInterface.Instance.switchToLibrary();
-}, 'opens the library to navigate through dashboards on Dash Mobile');
-ScriptingGlobals.add(function openMobileUploads() {
-    return MobileInterface.Instance.toggleUpload();
-}, 'opens the upload files menu for Dash Mobile');
-ScriptingGlobals.add(function switchToMobileUploadCollection() {
-    return MobileInterface.Instance.switchToMobileUploads();
-}, 'opens the mobile uploads collection on Dash Mobile');
-ScriptingGlobals.add(function openMobileAudio() {
-    return MobileInterface.Instance.toggleAudio();
-}, 'opens the record and dictate menu on Dash Mobile');
-ScriptingGlobals.add(function switchToMobilePresentation() {
-    return MobileInterface.Instance.setupDefaultPresentation();
-}, 'opens the presentation on Dash Mobile');
-ScriptingGlobals.add(function openMobileSettings() {
-    return SettingsManager.Instance.openMgr();
-}, 'opens settings on Dash Mobile');
-
-// Other global functions for mobile
-ScriptingGlobals.add(
-    function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) {
-        return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch);
-    },
-    'changes the active document displayed on the Dash Mobile',
-    '(doc: any)'
-);
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
deleted file mode 100644
index 07839b6f6..000000000
--- a/src/mobile/MobileMain.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as React from 'react';
-import * as ReactDOM from 'react-dom';
-import { DocServer } from '../client/DocServer';
-import { Docs } from '../client/documents/Documents';
-import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { AssignAllExtensions } from '../extensions/Extensions';
-import { MobileInterface } from './MobileInterface';
-
-AssignAllExtensions();
-
-(async () => {
-    const info = await CurrentUserUtils.loadCurrentUser();
-    DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
-    await Docs.Prototypes.initialize();
-    await CurrentUserUtils.loadUserDocument(info);
-    document.getElementById('root')!.addEventListener(
-        'wheel',
-        event => {
-            if (event.ctrlKey) {
-                event.preventDefault();
-            }
-        },
-        true
-    );
-    ReactDOM.render(<MobileInterface />, document.getElementById('root'));
-})();
diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss
deleted file mode 100644
index 7f286efc4..000000000
--- a/src/mobile/MobileMenu.scss
+++ /dev/null
@@ -1,271 +0,0 @@
-$navbar-height: 120px;
-$pathbar-height: 50px;
-
-* {
-    margin: 0px;
-    padding: 0px;
-    box-sizing: border-box;
-    font-family: "Open Sans";
-}
-
-body {
-    overflow: hidden;
-}
-
-.navbar {
-    position: fixed;
-    top: 0px;
-    left: 0px;
-    width: 100vw;
-    height: $navbar-height;
-    background-color: whitesmoke;
-    border-bottom: 5px solid black;
-}
-
-.navbar .toggle-btn {
-    position: absolute;
-    right: 20px;
-    top: 30px;
-    height: 70px;
-    width: 70px;
-    transition: all 300ms ease-in-out 200ms;
-}
-
-.navbar .header {
-    position: absolute;
-    top: 50%;
-    top: calc(9px + 50%);
-    right: 50%;
-    transform: translate(50%, -50%);
-    font-size: 40;
-    user-select: none;
-    text-transform: uppercase;
-    font-family: Arial, Helvetica, sans-serif;
-}
-
-.navbar .toggle-btn span {
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    width: 70%;
-    height: 4px;
-    background: black;
-    transition: all 200ms ease;
-}
-
-.navbar .toggle-btn span:nth-child(1) {
-    transition: top 200ms ease-in-out;
-    top: 30%;
-}
-
-.navbar .toggle-btn span:nth-child(3) {
-    transition: top 200ms ease-in-out;
-    top: 70%;
-}
-
-.navbar .toggle-btn.active {
-    transition: transform 200ms ease-in-out 200ms;
-    transform: rotate(135deg);
-}
-
-.navbar .toggle-btn.active span:nth-child(1) {
-    top: 50%;
-}
-
-.navbar .toggle-btn.active span:nth-child(2) {
-    transform: translate(-50%, -50%) rotate(90deg);
-}
-
-.navbar .toggle-btn.active span:nth-child(3) {
-    top: 50%;
-}
-// .navbar .home {
-//     position: relative;
-//     right: 5px;
-//     transform: translate(50%, -50%);
-//     font-size: 40;
-//     user-select: none;
-//     text-transform: uppercase;
-//     font-family: Arial, Helvetica, sans-serif;
-//     z-index: 200;
-// }
-
-.sidebar {
-    position: absolute;
-    top: 200px;
-    opacity: 0;
-    right: -100%;
-    width: 100%;
-    height: calc(100% - (200px));
-    z-index: 5;
-    background-color: whitesmoke;
-    transition: all 400ms ease 50ms;
-    padding: 20px;
-    // overflow-y: auto;
-    // -webkit-overflow-scrolling: touch;
-    
-    // border-right: 5px solid black;
-}
-
-.sidebar .item {
-    width: 100%;
-    padding: 13px 12px;
-    border-bottom: 1px solid rgba(200, 200, 200, 0.7);
-    font-family: Arial, Helvetica, sans-serif;
-    font-style: normal;
-    font-weight: normal;
-    user-select: none;
-    font-size: 35px;
-    text-transform: uppercase;
-    color: black;
-    
-}
-
-.sidebar .ink {
-    width: 100%;
-    padding: 13px 12px;
-    border-bottom: 1px solid rgba(200, 200, 200, 0.7);
-    font-family: Arial, Helvetica, sans-serif;
-    font-style: normal;
-    font-weight: normal;
-    user-select: none;
-    font-size: 35px;
-    text-transform: uppercase;
-    color: black;
-}
-
-.sidebar .ink:focus {
-    outline: 1px solid blue;
-}
-
-.sidebar .home {
-    position: absolute;
-    top: -135px;
-    right: calc(50% + 80px);
-    transform: translate(0%, -50%);
-    font-size: 40;
-    user-select: none;
-    text-transform: uppercase;
-    font-family: Arial, Helvetica, sans-serif;
-    z-index: 200;
-}
-
-.type {
-    display: inline;
-    text-transform: lowercase;
-    margin-left: 20px;
-    font-size: 35px;
-    font-style: italic;
-    color: rgb(28, 28, 28);
-}
-
-.right {
-    margin-left: 20px;
-    z-index: 200;
-}
-
-.left {
-    width: 100%;
-    height: 100%;
-}
-
-.sidebar .logout {
-    width: 100%;
-    padding: 13px 12px;
-    border-bottom: 1px solid rgba(200, 200, 200, 0.7);
-    font-family: Arial, Helvetica, sans-serif;
-    font-style: normal;
-    font-weight: normal;
-    user-select: none;
-    font-size: 30px;
-    text-transform: uppercase;
-    color: black;
-}
-
-.sidebar .settings {
-    width: 100%;
-    padding: 13px 12px;
-    border-bottom: 1px solid rgba(200, 200, 200, 0.7);
-    font-family: Arial, Helvetica, sans-serif;
-    font-style: normal;
-    font-weight: normal;
-    user-select: none;
-    font-size: 30px;
-    text-transform: uppercase;
-    color: black;
-}
-
-
-.sidebar.active {
-    right: 0%;
-    opacity: 1;
-}
-
-.back {
-    position: absolute;
-    top: -140px;
-    left: 50px;
-    transform: translate(0%, -50%);
-    color: black;
-    font-size: 60;
-    user-select: none;
-    text-transform: uppercase;
-    z-index: 100;
-    font-family: Arial, Helvetica, sans-serif;
-}
-
-
-.pathbar {
-    position: absolute;
-    top: 118px;
-    background: #1a1a1a;
-    z-index: 20;
-    border-radius: 0px;
-    width: 100%;
-    height: 80px;
-    transition: all 400ms ease 50ms;
-}
-
-.pathname {
-    position: relative;
-    font-size: 25;
-    top: 50%;
-    width: 90%;
-    left: 3%;
-    color: whitesmoke;
-    transform: translate(0%, -50%);
-    z-index: 20;
-    font-family: sans-serif;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    direction: rtl;
-    text-align: left;
-    text-transform: uppercase;
-}
-
-.homeContainer {
-    position: relative;
-    top: 200px;
-    height: calc(100% - 250px);
-    width: 90%;
-    overflow: scroll;
-    left: 5%;
-    background-color: lightpink;
-}
-
-.pinButton {
-    position: relative;
-    width: 100px;
-    height: 100px;
-    font-size: 90px;
-    text-align: center;
-    left: 50%;
-    transform: translate(-50%, 0);
-    border-style: solid;
-    border-radius: 50px;
-    border-width: medium;
-    background-color: pink;
-    z-index: 100;
-}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 9c74bf24e..e1afc64e5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -53,8 +53,6 @@ module.exports = {
         viewer: ['./src/debug/Viewer.tsx', 'webpack-hot-middleware/client?reload=true'],
         repl: ['./src/debug/Repl.tsx', 'webpack-hot-middleware/client?reload=true'],
         test: ['./src/debug/Test.tsx', 'webpack-hot-middleware/client?reload=true'],
-        inkControls: ['./src/mobile/InkControls.tsx', 'webpack-hot-middleware/client?reload=true'],
-        mobileInterface: ['./src/client/views/Main.tsx', 'webpack-hot-middleware/client?reload=true'],
     },
     devtool: 'source-map',
     output: {
@@ -119,6 +117,10 @@ module.exports = {
                     { loader: 'sass-loader' },
                 ],
             },
+            {
+                test: /\.(txt|d)$/i,
+                type: 'asset/source',
+            },
 
             // --------
             // SCSS MODULES - all have .module. in their name and can export to .tsx
-- 
cgit v1.2.3-70-g09d2

