From 9d4bfd04760753b6fdd7ed81372ab8d85b615bc3 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Fri, 7 Mar 2025 12:55:31 -0500
Subject: added more individual text formatting features + journal like
 background

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 40 ++++++++++++++--------
 1 file changed, 25 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index ec1f7a023..c8f049ecf 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -50,24 +50,19 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
     @action
     setDailyText() {
-        console.log('setDailyText() called...');
         const placeholderText = 'Start writing here...';
-        const initialText = `Journal Entry - ${this.journalDate}\n${placeholderText}`;
+        const dateText = `${this.journalDate}\n`;
 
         console.log('Checking if dataDoc has text field...');
 
-        const styles = {
-            bold: true, // Make the journal date bold
-            color: 'blue', // Set the journal date color to blue
-            fontSize: 18, // Set the font size to 18px for the whole text
-        };
-
-        console.log('Setting new text field with:', initialText);
-        this.dataDoc[this.fieldKey] = RichTextField.textToRtf(
-            initialText,
-            undefined, // No image DocId
-            styles, // Pass the styles object here
-            placeholderText.length // The position for text selection
+        this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
+            [
+                { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
+                { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
+                { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
+            ],
+            undefined,
+            placeholderText.length - 2
         );
 
         console.log('Current text field:', this.dataDoc[this.fieldKey]);
@@ -83,7 +78,22 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
     render() {
         return (
-            <div style={{ background: 'beige', width: '100%', height: '100%' }}>
+            <div
+                style={{
+                    // background: 'beige',
+                    width: '100%',
+                    height: '100%',
+                    backgroundColor: 'beige',
+                    backgroundImage: `
+                    repeating-linear-gradient(
+                        to bottom, 
+                        rgba(255, 26, 26, 0.2) 0px, rgba(255, 26, 26, 0.2) 1px,  /* Thin red stripes */
+                        transparent 1px, transparent 20px
+                    )
+                `,
+                    backgroundSize: '100% 20px',
+                    backgroundRepeat: 'repeat',
+                }}>
                 <FormattedTextBox {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 54bfa54b498f7a7c5813c2f5d0ff3005d6ff44ab Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Mar 2025 14:21:29 -0500
Subject: fixed selection of default journal text

---
 .../views/nodes/formattedText/DailyJournal.tsx     |  2 +-
 src/fields/RichTextField.ts                        | 50 ++++++----------------
 2 files changed, 14 insertions(+), 38 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index c8f049ecf..dfd19ae97 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -62,7 +62,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
             ],
             undefined,
-            placeholderText.length - 2
+            placeholderText.length
         );
 
         console.log('Current text field:', this.dataDoc[this.fieldKey]);
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 8f4d782b4..81b6c9ea9 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -87,57 +87,33 @@ export class RichTextField extends ObjectField {
             { type: 'text', anchor: 2 + plaintext.length - (selectBack ?? 0), head: 2 + plaintext.length }
         );
 
-
-    // AARAV ADD 
+    // AARAV ADD
 
     // takes in text segments instead of single text field
-    private static ToProsemirrorSegmented = (
-        textSegments: { text: string; styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string } }[],
-        imgDocId?: string,
-        selectBack?: number
-    ) =>
+    private static ToProsemirrorSegmented = (textSegments: { text: string; styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string } }[], imgDocId?: string, selectBack?: number) =>
         RichTextField.ToProsemirrorDoc(
             textSegments.map(seg => ({
                 type: 'paragraph', // Each segment becomes its own paragraph
-                content: [
-                    ...RichTextField.ToProsemirrorTextContent(seg.text, seg.styles),
-                    ...(imgDocId ? RichTextField.ToProsemirrorDashDocContent(imgDocId) : []),
-                ],
+                content: [...RichTextField.ToProsemirrorTextContent(seg.text, seg.styles), ...(imgDocId ? RichTextField.ToProsemirrorDashDocContent(imgDocId) : [])],
             })),
-            {
+            (textLen => ({
                 type: 'text',
-                anchor: 2 + textSegments.map(seg => seg.text).join('').length - (selectBack ?? 0),
-                head: 2 + textSegments.map(seg => seg.text).join('').length,
-            }
+                anchor: textLen - (selectBack ?? 0),
+                head: textLen,
+            }))(2 * textSegments.length + textSegments.map(seg => seg.text).join('').length - 1)
+            // selection/doc end = text length + 2 for each paragraph. subtract 1 to set selection inside of end of last paragraph
         );
-        
-    // AARAV ADD ||
-
 
+    // AARAV ADD ||
 
-    public static textToRtf(
-        text: string, 
-        imgDocId?: string, 
-        styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string }, 
-        selectBack?: number
-    ) {
-        return new RichTextField(
-            JSON.stringify(RichTextField.ToProsemirror(text, imgDocId, styles, selectBack)), text);
+    public static textToRtf(text: string, imgDocId?: string, styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string }, selectBack?: number) {
+        return new RichTextField(JSON.stringify(RichTextField.ToProsemirror(text, imgDocId, styles, selectBack)), text);
     }
 
     // AARAV ADD
-    public static textToRtfFormat(
-        textSegments: { text: string; styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string } }[], 
-        imgDocId?: string, 
-        selectBack?: number
-    ) {
-        return new RichTextField(
-            JSON.stringify(RichTextField.ToProsemirrorSegmented(textSegments, imgDocId, selectBack)), 
-            textSegments.map(seg => seg.text).join(''));
+    public static textToRtfFormat(textSegments: { text: string; styles?: { bold?: boolean; italic?: boolean; fontSize?: number; color?: string } }[], imgDocId?: string, selectBack?: number) {
+        return new RichTextField(JSON.stringify(RichTextField.ToProsemirrorSegmented(textSegments, imgDocId, selectBack)), textSegments.map(seg => seg.text).join(''));
     }
 
     // AARAV ADD
-
-
-
 }
-- 
cgit v1.2.3-70-g09d2


From 97f2fd0fe18bf476304af06918de25059d4fd8b5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Mar 2025 21:16:44 -0500
Subject: fixes to daily journal creation.

---
 src/client/documents/DocUtils.ts                   | 79 +++++++++++++++-------
 src/client/documents/Documents.ts                  | 36 +++++++---
 .../views/nodes/formattedText/DailyJournal.tsx     | 62 +----------------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 28 ++++----
 4 files changed, 93 insertions(+), 112 deletions(-)

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index b216551d5..914e94bfa 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -714,32 +714,59 @@ export namespace DocUtils {
                       nativeWidth: 40,
                       nativeHeight: 40,
                   })
-                : Docs.Create.TextDocument('', {
-                      annotationOn,
-                      backgroundColor,
-                      x,
-                      y,
-                      title,
-                      ...(defaultTextTemplate
-                          ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
-                          : {
-                                _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
-                                _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
-                                _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
-                                _layout_fitWidth: true,
-                                _layout_autoHeight: true,
-                                backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
-                                borderColor: Doc.UserDoc().borderColor as string,
-                                borderWidth: Doc.UserDoc().borderWidth as number,
-                                text_fitBox: BoolCast(Doc.UserDoc().fitBox),
-                                text_align: StrCast(Doc.UserDoc().textAlign),
-                                text_fontColor: StrCast(Doc.UserDoc().fontColor),
-                                text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
-                                text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
-                                text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
-                                text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
-                            }),
-                  });
+                : defaultTextTemplate?.type === DocumentType.JOURNAL
+                  ? Docs.Create.DailyJournalDocument('', {
+                        annotationOn,
+                        backgroundColor,
+                        x,
+                        y,
+                        title,
+                        ...(defaultTextTemplate
+                            ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+                            : {
+                                  _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
+                                  _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
+                                  _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
+                                  _layout_fitWidth: true,
+                                  _layout_autoHeight: true,
+                                  backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
+                                  borderColor: Doc.UserDoc().borderColor as string,
+                                  borderWidth: Doc.UserDoc().borderWidth as number,
+                                  text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+                                  text_align: StrCast(Doc.UserDoc().textAlign),
+                                  text_fontColor: StrCast(Doc.UserDoc().fontColor),
+                                  text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+                                  text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
+                                  text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
+                                  text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
+                              }),
+                    })
+                  : Docs.Create.TextDocument('', {
+                        annotationOn,
+                        backgroundColor,
+                        x,
+                        y,
+                        title,
+                        ...(defaultTextTemplate
+                            ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+                            : {
+                                  _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
+                                  _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
+                                  _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
+                                  _layout_fitWidth: true,
+                                  _layout_autoHeight: true,
+                                  backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
+                                  borderColor: Doc.UserDoc().borderColor as string,
+                                  borderWidth: Doc.UserDoc().borderWidth as number,
+                                  text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+                                  text_align: StrCast(Doc.UserDoc().textAlign),
+                                  text_fontColor: StrCast(Doc.UserDoc().fontColor),
+                                  text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+                                  text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
+                                  text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
+                                  text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
+                              }),
+                    });
 
         if (defaultTextTemplate) {
             tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 61b370da4..7468b0c00 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -920,22 +920,36 @@ export namespace Docs {
         // AARAV ADD //
 
         export function DailyJournalDocument(text: string | RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
-            const styles = {
-                bold: true, // Make the journal date bold
-                color: 'blue', // Set the journal date color to blue
-                fontSize: 18, // Set the font size to 18px for the whole text
+            const getFormattedDate = () => {
+                const date = new Date().toLocaleDateString(undefined, {
+                    weekday: 'long',
+                    year: 'numeric',
+                    month: 'long',
+                    day: 'numeric',
+                });
+                return date;
+            };
+
+            const getDailyText = () => {
+                const placeholderText = 'Start writing here...';
+                const dateText = `${getFormattedDate()}`;
+
+                return RichTextField.textToRtfFormat(
+                    [
+                        { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
+                        { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
+                        { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
+                    ],
+                    undefined,
+                    placeholderText.length
+                );
             };
 
             return InstanceFromProto(
                 Prototypes.get(DocumentType.JOURNAL),
-                typeof text === 'string' ? RichTextField.textToRtf(text, undefined, styles, undefined) : text,
+                getDailyText(),
                 {
-                    title: new Date().toLocaleDateString(undefined, {
-                        weekday: 'long',
-                        year: 'numeric',
-                        month: 'long',
-                        day: 'numeric',
-                    }),
+                    title: getFormattedDate(),
                     ...options,
                 },
                 undefined,
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index dfd19ae97..51e9d9ec1 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -1,6 +1,5 @@
-import { action, makeObservable, observable } from 'mobx';
+import { makeObservable } from 'mobx';
 import * as React from 'react';
-import { RichTextField } from '../../../../fields/RichTextField';
 import { Docs } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
@@ -8,8 +7,6 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
-    @observable journalDate: string;
-
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(DailyJournal, fieldStr);
     }
@@ -17,63 +14,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     constructor(props: FormattedTextBoxProps) {
         super(props);
         makeObservable(this);
-        this.journalDate = this.getFormattedDate();
-
-        console.log('Constructor: Setting initial title and text...');
-        this.setDailyTitle();
-        this.setDailyText();
-    }
-
-    getFormattedDate(): string {
-        const date = new Date().toLocaleDateString(undefined, {
-            weekday: 'long',
-            year: 'numeric',
-            month: 'long',
-            day: 'numeric',
-        });
-        console.log('getFormattedDate():', date);
-        return date;
-    }
-
-    @action
-    setDailyTitle() {
-        console.log('setDailyTitle() called...');
-        console.log('Current title before update:', this.dataDoc.title);
-
-        if (!this.dataDoc.title || this.dataDoc.title !== this.journalDate) {
-            console.log('Updating title to:', this.journalDate);
-            this.dataDoc.title = this.journalDate;
-        }
-
-        console.log('New title after update:', this.dataDoc.title);
-    }
-
-    @action
-    setDailyText() {
-        const placeholderText = 'Start writing here...';
-        const dateText = `${this.journalDate}\n`;
-
-        console.log('Checking if dataDoc has text field...');
-
-        this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
-            [
-                { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
-                { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
-                { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
-            ],
-            undefined,
-            placeholderText.length
-        );
-
-        console.log('Current text field:', this.dataDoc[this.fieldKey]);
-    }
-
-    componentDidMount(): void {
-        console.log('componentDidMount() triggered...');
-        // bcz: This should be moved into Docs.Create.DailyJournalDocument()
-        // otherwise, it will override all the text whenever the note is reloaded
-        this.setDailyTitle();
-        this.setDailyText();
     }
 
     render() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a0eb6067e..2a54cca90 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2,9 +2,10 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
+import { Property } from 'csstype';
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { baseKeymap, selectAll } from 'prosemirror-commands';
+import { baseKeymap, selectAll, splitBlock } from 'prosemirror-commands';
 import { history } from 'prosemirror-history';
 import { inputRules } from 'prosemirror-inputrules';
 import { keymap } from 'prosemirror-keymap';
@@ -49,12 +50,14 @@ import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup';
 import { PinDocView, PinProps } from '../../PinFuncs';
 import { SidebarAnnos } from '../../SidebarAnnos';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
 import { StyleProp } from '../../StyleProp';
 import { styleFromLayoutString } from '../../StyleProvider';
 import { mediaState } from '../AudioBox';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FocusViewOptions } from '../FocusViewOptions';
+import { LabelBox } from '../LabelBox';
 import { LinkInfo } from '../LinkDocPreview';
 import { OpenWhere } from '../OpenWhere';
 import './FormattedTextBox.scss';
@@ -64,9 +67,6 @@ import { removeMarkWithAttrs } from './prosemirrorPatches';
 import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
-import { Property } from 'csstype';
-import { LabelBox } from '../LabelBox';
-import { StickerPalette } from '../../smartdraw/StickerPalette';
 // import * as applyDevTools from 'prosemirror-dev-tools';
 
 export interface FormattedTextBoxProps extends FieldViewProps {
@@ -449,7 +449,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             Doc.MyPublishedDocs.filter(term => term.title).forEach(term => {
                 tr = this.hyperlinkTerm(tr, term, newAutoLinks);
             });
-            tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)));
+            const marks = tr.storedMarks;
+            tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to))).setStoredMarks(marks);
             this.EditorView?.dispatch(tr);
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc));
@@ -1541,25 +1542,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             this._editorView.TextView = this;
         }
 
-        const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, DocumentView.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
+        const selectOnLoad =
+            Doc.AreProtosEqual(this._props.TemplateDataDocument ?? DocCast(this.Document.rootDocument, this.Document), DocumentView.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
         const selLoadChar = FormattedTextBox.SelectOnLoadChar;
         if (selectOnLoad) {
             DocumentView.SetSelectOnLoad(undefined);
             FormattedTextBox.SelectOnLoadChar = '';
         }
         if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
-            const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
+            const { $from } = this.EditorView.state.selection;
             const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
             const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
             const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-            let { tr } = this.EditorView.state;
-            if (selLoadChar) {
-                const tr1 = this.EditorView.state.tr.setStoredMarks(storedMarks);
-                tr = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
-            }
-            this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size - 1))).setStoredMarks(storedMarks));
+            if (selLoadChar === 'Enter') {
+                splitBlock(this.EditorView.state, (tx3: Transaction) => this.EditorView?.dispatch(tx3.setStoredMarks(storedMarks)));
+            } else if (selLoadChar) {
+                this.EditorView.dispatch(this.EditorView.state.tr.replaceSelectionWith(this.EditorView.state.schema.text(selLoadChar, storedMarks))); // prettier-ignore
+            } else this.EditorView.dispatch(this.EditorView.state.tr.setStoredMarks(storedMarks));
             this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
-            console.log(this.EditorView.state);
         }
         if (selectOnLoad) {
             this.EditorView!.focus();
-- 
cgit v1.2.3-70-g09d2


From 9c5d14fdd562dc1bcc8aa0f73ce7ad189c9fbf23 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 19 Mar 2025 19:05:43 -0400
Subject: daily journal title/text update fixes + GPT prompts button for
 journalling prompts integration

---
 src/client/documents/Documents.ts                  |  52 ++++----
 .../views/nodes/formattedText/DailyJournal.tsx     | 145 ++++++++++++++++++++-
 2 files changed, 169 insertions(+), 28 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fe121021d..668725d2b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -920,36 +920,36 @@ export namespace Docs {
         // AARAV ADD //
 
         export function DailyJournalDocument(text: string | RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
-            const getFormattedDate = () => {
-                const date = new Date().toLocaleDateString(undefined, {
-                    weekday: 'long',
-                    year: 'numeric',
-                    month: 'long',
-                    day: 'numeric',
-                });
-                return date;
-            };
-
-            const getDailyText = () => {
-                const placeholderText = 'Start writing here...';
-                const dateText = `${getFormattedDate()}`;
-
-                return RichTextField.textToRtfFormat(
-                    [
-                        { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
-                        { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
-                        { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
-                    ],
-                    undefined,
-                    placeholderText.length
-                );
-            };
+            // const getFormattedDate = () => {
+            //     const date = new Date().toLocaleDateString(undefined, {
+            //         weekday: 'long',
+            //         year: 'numeric',
+            //         month: 'long',
+            //         day: 'numeric',
+            //     });
+            //     return date;
+            // };
+
+            // const getDailyText = () => {
+            //     const placeholderText = 'Start writing here...';
+            //     const dateText = `${getFormattedDate()}`;
+
+            //     return RichTextField.textToRtfFormat(
+            //         [
+            //             { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
+            //             { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
+            //             { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
+            //         ],
+            //         undefined,
+            //         placeholderText.length
+            //     );
+            // };
 
             return InstanceFromProto(
                 Prototypes.get(DocumentType.JOURNAL),
-                getDailyText(),
+                "",
                 {
-                    title: getFormattedDate(),
+                    title: "",
                     ...options,
                 },
                 undefined,
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 51e9d9ec1..31108f05a 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -1,12 +1,17 @@
-import { makeObservable } from 'mobx';
+import { makeObservable, action, observable, autorun } from 'mobx';
 import * as React from 'react';
 import { Docs } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
+import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { TextSelection } from 'prosemirror-state';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+    @observable journalDate: string;
+
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(DailyJournal, fieldStr);
     }
@@ -14,8 +19,126 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     constructor(props: FormattedTextBoxProps) {
         super(props);
         makeObservable(this);
+        this.journalDate = this.getFormattedDate();
+    }
+
+    getFormattedDate(): string {
+        const date = new Date().toLocaleDateString(undefined, {
+            weekday: 'long',
+            year: 'numeric',
+            month: 'long',
+            day: 'numeric',
+        });
+        console.log('getFormattedDate():', date);
+        return date;
+    }
+
+    @action
+    setDailyTitle() {
+        console.log('setDailyTitle() called...');
+        console.log('Current title before update:', this.dataDoc.title);
+
+        if (!this.dataDoc.title || this.dataDoc.title !== this.journalDate) {
+            console.log('Updating title to:', this.journalDate);
+            this.dataDoc.title = this.journalDate;
+        }
+
+        console.log('New title after update:', this.dataDoc.title);
+    }
+
+    @action
+    setDailyText() {
+        const placeholderText = 'Start writing here...';
+        const dateText = `${this.journalDate}\n`;
+
+        console.log('Checking if dataDoc has text field...');
+
+        this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
+            [
+                { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
+                { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
+                { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
+            ],
+            undefined,
+            placeholderText.length
+        );
+
+        console.log('Current text field:', this.dataDoc[this.fieldKey]);
+    }
+
+    componentDidMount(): void {
+        console.log('componentDidMount() triggered...');
+        console.log("Text: " + (this.Document.text as any)?.Text);
+
+        const rawText = (this.Document.text as any)?.Text ?? '';
+        const isTextEmpty = !rawText || rawText === "";
+
+        const currentTitle = this.dataDoc.title || "";
+        const isTitleString = typeof currentTitle === 'string';
+        const isDefaultTitle = isTitleString && currentTitle.includes("Untitled DailyJournal");
+
+        if (isTextEmpty && isDefaultTitle) {
+            console.log('Journal title and text are default. Initializing...');
+            this.setDailyTitle();
+            this.setDailyText();
+        } else {
+            console.log('Journal already has content. Skipping initialization.');
+        }
     }
 
+    @action handleGeneratePrompts = async () => {
+        const rawText = (this.Document.text as any)?.Text ?? '';
+        console.log('Extracted Journal Text:', rawText);
+        console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
+
+        if (!rawText.trim()) {
+            alert('Journal is empty! Write something first.');
+            return;
+        }
+
+        try {
+            // Call GPT API to generate prompts
+            const res = await gptAPICall('Generate 1-2 short journal prompts for the following journal entry: ' + rawText, GPTCallType.COMPLETION);
+
+            if (!res) {
+                console.error('GPT call failed.');
+                return;
+            }
+
+            const editorView = this._ref.current?.EditorView;
+            if (!editorView) {
+                console.error('EditorView is not available.');
+                return;
+            } 
+
+            else {
+                const { state, dispatch } = editorView;
+                const { schema } = state;
+
+                // Use available marks 
+                const boldMark = schema.marks.strong.create();
+                const italicMark = schema.marks.em.create();
+                const fontSizeMark = schema.marks.pFontSize.create({ fontSize: "14px" });
+                const fontColorMark = schema.marks.pFontColor.create({ fontColor: "gray" });
+
+                // Create text nodes with formatting
+                const headerText = schema.text('\n\n# Suggested Prompts:\n', [boldMark, italicMark, fontSizeMark, fontColorMark]);
+                const responseText = schema.text(res, [fontSizeMark, fontColorMark]);
+
+                // Insert formatted text
+                const transaction = state.tr.insert(
+                    state.selection.from, headerText).insert(
+                        state.selection.from + headerText.nodeSize, responseText);
+                dispatch(transaction);
+            }
+
+        } catch (err) {
+            console.error('Error calling GPT:', err);
+        }
+    };
+
+    _ref = React.createRef<FormattedTextBox>();
+
     render() {
         return (
             <div
@@ -34,7 +157,25 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                     backgroundSize: '100% 20px',
                     backgroundRepeat: 'repeat',
                 }}>
-                <FormattedTextBox {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
+                {/* GPT Button */}
+                <button
+                    style={{
+                        position: 'absolute',
+                        bottom: '5px',
+                        right: '5px',
+                        padding: '5px 10px',
+                        backgroundColor: '#9EAD7C',
+                        color: 'white',
+                        border: 'none',
+                        borderRadius: '5px',
+                        cursor: 'pointer',
+                        zIndex: 10,
+                    }}
+                    onClick={this.handleGeneratePrompts}>
+                    Prompts
+                </button>
+
+                <FormattedTextBox ref={this._ref} {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 5315ae28692e8214e6661d2336b6679fed9f90e2 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Thu, 3 Apr 2025 22:25:13 -0400
Subject: code cleanup + added basic functionality for predictive text (only
 mock text right now, and doesn't automatically disappear yet)

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 119 ++++++++++++++++++---
 1 file changed, 103 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 31108f05a..697cf4a2a 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -11,6 +11,9 @@ import { TextSelection } from 'prosemirror-state';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable journalDate: string;
+    @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay
+    @observable lastUserText: string = ''; // Store last user-entered text
+    _ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
 
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(DailyJournal, fieldStr);
@@ -22,6 +25,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         this.journalDate = this.getFormattedDate();
     }
 
+    /**
+     * Method to get the current date in standard format
+     * @returns - date in standard long format
+     */
+
     getFormattedDate(): string {
         const date = new Date().toLocaleDateString(undefined, {
             weekday: 'long',
@@ -33,6 +41,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         return date;
     }
 
+    /**
+     * Method to set the title of the node to the date
+     */
     @action
     setDailyTitle() {
         console.log('setDailyTitle() called...');
@@ -46,6 +57,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         console.log('New title after update:', this.dataDoc.title);
     }
 
+    /**
+     * Method to set the standard text of the node (to the current date)
+     */
     @action
     setDailyText() {
         const placeholderText = 'Start writing here...';
@@ -66,16 +80,88 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         console.log('Current text field:', this.dataDoc[this.fieldKey]);
     }
 
+    /**
+     * Tracks user typing text inout into the node, to call the insert predicted
+     * text function when appropriate (i.e. when the user stops typing)
+     */
+
+    @action onTextInput = () => {
+        const editorView = this._ref.current?.EditorView;
+        if (!editorView) return;
+
+        const { state } = editorView;
+        const currentText = state.doc.textBetween(0, state.doc.content.size);
+
+        // Store the last user text (before adding the prediction)
+        this.lastUserText = currentText;
+
+        // If user types, clear any existing timeout
+        if (this.typingTimeout) clearTimeout(this.typingTimeout);
+
+        // Set a new timeout to add predictive text after 3.5 seconds
+        this.typingTimeout = setTimeout(() => {
+            this.insertPredictiveQuestion();
+        }, 3500); // 3.5 seconds
+    };
+
+    /**
+     * Inserts predictive text at the end of what the user is typing
+     */
+
+    @action insertPredictiveQuestion = () => {
+        const editorView = this._ref.current?.EditorView;
+        if (!editorView) return;
+
+        const { state, dispatch } = editorView;
+        const { schema } = state;
+        const { from, to } = state.selection;
+        const insertPos = from === to ? from : to; // cursor position
+
+        const resolvedPos = state.doc.resolve(insertPos);
+        const parentNode = resolvedPos.parent;
+        const indexInParent = resolvedPos.index();
+
+        const isAtEndOfParent = indexInParent >= parentNode.childCount;
+
+        // Check if there's a line break or paragraph node after the current position
+        let hasNewlineAfter = false;
+        try {
+            const nextNode = parentNode.child(indexInParent);
+            hasNewlineAfter = nextNode.type.name === 'hard_break' || nextNode.type.name === 'paragraph';
+        } catch {
+            hasNewlineAfter = false;
+        }
+
+        // Only insert if we're at end of node, or there's a newline node after
+        if (!isAtEndOfParent && !hasNewlineAfter) return;
+
+        const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
+        const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'lightgray' });
+        const fontItalicsMark = schema.marks.em.create();
+
+        // styled text node
+        const predictedText = schema.text(' ... why?', [fontSizeMark, fontColorMark, fontItalicsMark]);
+
+        // Insert styled text at cursor position
+        const transaction = state.tr.insert(insertPos, predictedText);
+        dispatch(transaction);
+    };
+
     componentDidMount(): void {
         console.log('componentDidMount() triggered...');
-        console.log("Text: " + (this.Document.text as any)?.Text);
+        console.log('Text: ' + (this.Document.text as any)?.Text);
+
+        const editorView = this._ref.current?.EditorView;
+        if (editorView) {
+            editorView.dom.addEventListener('input', this.onTextInput);
+        }
 
         const rawText = (this.Document.text as any)?.Text ?? '';
-        const isTextEmpty = !rawText || rawText === "";
+        const isTextEmpty = !rawText || rawText === '';
 
-        const currentTitle = this.dataDoc.title || "";
+        const currentTitle = this.dataDoc.title || '';
         const isTitleString = typeof currentTitle === 'string';
-        const isDefaultTitle = isTitleString && currentTitle.includes("Untitled DailyJournal");
+        const isDefaultTitle = isTitleString && currentTitle.includes('Untitled DailyJournal');
 
         if (isTextEmpty && isDefaultTitle) {
             console.log('Journal title and text are default. Initializing...');
@@ -86,6 +172,14 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         }
     }
 
+    componentWillUnmount(): void {
+        const editorView = this._ref.current?.EditorView;
+        if (editorView) {
+            editorView.dom.removeEventListener('input', this.onTextInput);
+        }
+        if (this.typingTimeout) clearTimeout(this.typingTimeout);
+    }
+
     @action handleGeneratePrompts = async () => {
         const rawText = (this.Document.text as any)?.Text ?? '';
         console.log('Extracted Journal Text:', rawText);
@@ -109,36 +203,29 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
             if (!editorView) {
                 console.error('EditorView is not available.');
                 return;
-            } 
-
-            else {
+            } else {
                 const { state, dispatch } = editorView;
                 const { schema } = state;
 
-                // Use available marks 
+                // Use available marks
                 const boldMark = schema.marks.strong.create();
                 const italicMark = schema.marks.em.create();
-                const fontSizeMark = schema.marks.pFontSize.create({ fontSize: "14px" });
-                const fontColorMark = schema.marks.pFontColor.create({ fontColor: "gray" });
+                const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
+                const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'gray' });
 
                 // Create text nodes with formatting
                 const headerText = schema.text('\n\n# Suggested Prompts:\n', [boldMark, italicMark, fontSizeMark, fontColorMark]);
                 const responseText = schema.text(res, [fontSizeMark, fontColorMark]);
 
                 // Insert formatted text
-                const transaction = state.tr.insert(
-                    state.selection.from, headerText).insert(
-                        state.selection.from + headerText.nodeSize, responseText);
+                const transaction = state.tr.insert(state.selection.from, headerText).insert(state.selection.from + headerText.nodeSize, responseText);
                 dispatch(transaction);
             }
-
         } catch (err) {
             console.error('Error calling GPT:', err);
         }
     };
 
-    _ref = React.createRef<FormattedTextBox>();
-
     render() {
         return (
             <div
-- 
cgit v1.2.3-70-g09d2


From 243449296aad3d5f825e0d7bee6be42ae547a2eb Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Fri, 4 Apr 2025 12:19:27 -0400
Subject: predictive text disappears on type/click someplace else

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 64 ++++++++++++++++++----
 1 file changed, 52 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 697cf4a2a..da96b8b2f 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -8,12 +8,15 @@ import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
 import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { TextSelection } from 'prosemirror-state';
+import { Plugin } from 'prosemirror-state';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable journalDate: string;
     @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay
     @observable lastUserText: string = ''; // Store last user-entered text
     _ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
+    predictiveTextRange: { from: number; to: number } | null = null; // where predictive text starts and ends
+    private predictiveText: string | null = ' ... why?';
 
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(DailyJournal, fieldStr);
@@ -89,19 +92,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const editorView = this._ref.current?.EditorView;
         if (!editorView) return;
 
-        const { state } = editorView;
-        const currentText = state.doc.textBetween(0, state.doc.content.size);
-
-        // Store the last user text (before adding the prediction)
-        this.lastUserText = currentText;
-
-        // If user types, clear any existing timeout
         if (this.typingTimeout) clearTimeout(this.typingTimeout);
 
-        // Set a new timeout to add predictive text after 3.5 seconds
         this.typingTimeout = setTimeout(() => {
             this.insertPredictiveQuestion();
-        }, 3500); // 3.5 seconds
+        }, 3500);
     };
 
     /**
@@ -115,12 +110,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const { state, dispatch } = editorView;
         const { schema } = state;
         const { from, to } = state.selection;
-        const insertPos = from === to ? from : to; // cursor position
+        const insertPos = to; // cursor position
 
         const resolvedPos = state.doc.resolve(insertPos);
         const parentNode = resolvedPos.parent;
         const indexInParent = resolvedPos.index();
-
         const isAtEndOfParent = indexInParent >= parentNode.childCount;
 
         // Check if there's a line break or paragraph node after the current position
@@ -140,11 +134,50 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const fontItalicsMark = schema.marks.em.create();
 
         // styled text node
-        const predictedText = schema.text(' ... why?', [fontSizeMark, fontColorMark, fontItalicsMark]);
+        const text = ' ... why?';
+        const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);
 
         // Insert styled text at cursor position
         const transaction = state.tr.insert(insertPos, predictedText);
         dispatch(transaction);
+
+        this.predictiveText = text;
+    };
+
+    createPredictiveCleanupPlugin = () => {
+        return new Plugin({
+            view: () => {
+                return {
+                    update: (view, prevState) => {
+                        const { state, dispatch } = view;
+                        if (!this.predictiveText) return;
+
+                        // Check if doc or selection changed
+                        if (!prevState.doc.eq(state.doc) || !prevState.selection.eq(state.selection)) {
+                            let found = false;
+                            const textToRemove = this.predictiveText;
+
+                            state.doc.descendants((node, pos) => {
+                                if (node.isText && node.text === textToRemove) {
+                                    // Remove the predictive text node
+                                    const tr = state.tr.delete(pos, pos + node.nodeSize);
+                                    dispatch(tr);
+                                    this.predictiveText = null;
+                                    found = true;
+                                    return false; // stop traversal
+                                }
+                                return true;
+                            });
+
+                            if (!found) {
+                                // fallback cleanup
+                                this.predictiveText = null;
+                            }
+                        }
+                    },
+                };
+            },
+        });
     };
 
     componentDidMount(): void {
@@ -154,6 +187,13 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const editorView = this._ref.current?.EditorView;
         if (editorView) {
             editorView.dom.addEventListener('input', this.onTextInput);
+
+            // Add plugin to state if not already added
+            const cleanupPlugin = this.createPredictiveCleanupPlugin();
+            const newState = editorView.state.reconfigure({
+                plugins: [...editorView.state.plugins, cleanupPlugin],
+            });
+            editorView.updateState(newState);
         }
 
         const rawText = (this.Document.text as any)?.Text ?? '';
-- 
cgit v1.2.3-70-g09d2


From f99bf9889fb1c84f211312f478df6046bf1b6b6e Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Fri, 4 Apr 2025 16:33:16 -0400
Subject: text color defaults once predicted text disappears

---
 src/client/views/nodes/formattedText/DailyJournal.tsx | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index da96b8b2f..f308d45a8 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -159,12 +159,18 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
                             state.doc.descendants((node, pos) => {
                                 if (node.isText && node.text === textToRemove) {
-                                    // Remove the predictive text node
                                     const tr = state.tr.delete(pos, pos + node.nodeSize);
+
+                                    // Set the desired default marks for future input
+                                    const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' });
+                                    const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' });
+                                    tr.setStoredMarks([]);
+                                    tr.setStoredMarks([fontSizeMark, fontColorMark]);
+
                                     dispatch(tr);
+
                                     this.predictiveText = null;
-                                    found = true;
-                                    return false; // stop traversal
+                                    return false;
                                 }
                                 return true;
                             });
-- 
cgit v1.2.3-70-g09d2


From b6823909532bdc727a51b8bda266cf62dd5dff6d Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Fri, 4 Apr 2025 21:43:40 -0400
Subject: predictive question replaced from mock text to GPT (actual
 prediction)

---
 src/client/views/nodes/formattedText/DailyJournal.tsx | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index f308d45a8..26a86bc6e 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -103,7 +103,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * Inserts predictive text at the end of what the user is typing
      */
 
-    @action insertPredictiveQuestion = () => {
+    @action insertPredictiveQuestion = async () => {
         const editorView = this._ref.current?.EditorView;
         if (!editorView) return;
 
@@ -133,8 +133,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'lightgray' });
         const fontItalicsMark = schema.marks.em.create();
 
+        this.predictiveText = ' ...'; // placeholder for now
+
+        const fullTextUpToCursor = state.doc.textBetween(0, state.selection.to, '\n', '\n');
+        const gptPrompt = `Given the following incomplete journal entry, generate a single 2-5 word question that continues the user's thought:\n\n"${fullTextUpToCursor}"`;
+        const res = await gptAPICall(gptPrompt, GPTCallType.COMPLETION);
+        if (!res) return;
+
         // styled text node
-        const text = ' ... why?';
+        const text = ` ... ${res.trim()}`;
         const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);
 
         // Insert styled text at cursor position
-- 
cgit v1.2.3-70-g09d2


From 75e76f1ec098ea9cbcd76432002da1bb73d74eba Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Apr 2025 14:44:07 -0400
Subject: fixed removing predictive prompt.  fixed first letter typed after
 predictive prompt

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 23 ++++++++++------------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 19 +++++++++++++++---
 2 files changed, 26 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 26a86bc6e..797a9b812 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -1,14 +1,14 @@
-import { makeObservable, action, observable, autorun } from 'mobx';
+import { makeObservable, action, observable } from 'mobx';
 import * as React from 'react';
 import { Docs } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
-import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
+import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 import { RichTextField } from '../../../../fields/RichTextField';
-import { TextSelection } from 'prosemirror-state';
 import { Plugin } from 'prosemirror-state';
+import { RTFCast } from '../../../../fields/Types';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable journalDate: string;
@@ -109,7 +109,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
         const { state, dispatch } = editorView;
         const { schema } = state;
-        const { from, to } = state.selection;
+        const { to } = state.selection;
         const insertPos = to; // cursor position
 
         const resolvedPos = state.doc.resolve(insertPos);
@@ -121,7 +121,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         let hasNewlineAfter = false;
         try {
             const nextNode = parentNode.child(indexInParent);
-            hasNewlineAfter = nextNode.type.name === 'hard_break' || nextNode.type.name === 'paragraph';
+            hasNewlineAfter = nextNode.type.name === schema.nodes.hard_break.name || nextNode.type.name === schema.nodes.paragraph.name;
         } catch {
             hasNewlineAfter = false;
         }
@@ -145,7 +145,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);
 
         // Insert styled text at cursor position
-        const transaction = state.tr.insert(insertPos, predictedText);
+        const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks([state.schema.marks.pFontColor.create({ fontColor: 'gray' })]); // should probably instead inquire marks before predictive prompt
         dispatch(transaction);
 
         this.predictiveText = text;
@@ -195,7 +195,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
     componentDidMount(): void {
         console.log('componentDidMount() triggered...');
-        console.log('Text: ' + (this.Document.text as any)?.Text);
+        console.log('Text: ' + RTFCast(this.Document.text)?.Text);
 
         const editorView = this._ref.current?.EditorView;
         if (editorView) {
@@ -203,13 +203,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
             // Add plugin to state if not already added
             const cleanupPlugin = this.createPredictiveCleanupPlugin();
-            const newState = editorView.state.reconfigure({
-                plugins: [...editorView.state.plugins, cleanupPlugin],
-            });
-            editorView.updateState(newState);
+            this._ref.current?.addPlugin(cleanupPlugin);
         }
 
-        const rawText = (this.Document.text as any)?.Text ?? '';
+        const rawText = RTFCast(this.Document.text)?.Text ?? '';
         const isTextEmpty = !rawText || rawText === '';
 
         const currentTitle = this.dataDoc.title || '';
@@ -234,7 +231,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     }
 
     @action handleGeneratePrompts = async () => {
-        const rawText = (this.Document.text as any)?.Text ?? '';
+        const rawText = RTFCast(this.Document.text)?.Text ?? '';
         console.log('Extracted Journal Text:', rawText);
         console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a45b8a488..bef999e6d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -78,7 +78,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
-    public static MakeConfig(rules?: RichTextRules, textBox?: FormattedTextBox) {
+    public static MakeConfig(rules?: RichTextRules, textBox?: FormattedTextBox, plugs?: Plugin[]) {
         return {
             schema,
             plugins: [
@@ -89,6 +89,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 keymap(baseKeymap),
                 new Plugin({ props: { attributes: { class: 'ProseMirror-example-setup-style' } } }),
                 new Plugin({ view: () => new FormattedTextBoxComment() }),
+                ...(plugs ?? []),
             ],
         };
     }
@@ -140,6 +141,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     public ApplyingChange: string = '';
 
     @observable _showSidebar = false;
+    @observable _userPlugins: Plugin[] = [];
 
     @computed get fontColor()      { return this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.FontColor) as string; } // prettier-ignore
     @computed get fontSize()       { return this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.FontSize) as string; } // prettier-ignore
@@ -153,7 +155,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     // eslint-disable-next-line no-return-assign
-    @computed get config()                     { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this); } // prettier-ignore
+    @computed get config()                     { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this, this._userPlugins ?? []); } // prettier-ignore
     @computed get recordingDictation()         { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore
     @computed get SidebarShown()               { return !!(this._showSidebar || this.layoutDoc._layout_showSidebar);  } // prettier-ignore
     @computed get allSidebarDocs()             { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore
@@ -1224,6 +1226,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         if (nh) this.layoutDoc._nativeHeight = scrollHeight;
     };
 
+    addPlugin = (plugin: Plugin) => {
+        const editorView = this.EditorView;
+        if (editorView) {
+            this._userPlugins.push(plugin);
+            const newState = editorView.state.reconfigure({
+                plugins: [...editorView.state.plugins, plugin],
+            });
+            editorView.updateState(newState);
+        }
+    };
+
     @computed get tagsHeight() {
         return this.DocumentView?.().showTags ? Math.max(0, 20 - Math.max(this._props.yPadding ?? 0, NumCast(this.layoutDoc._yMargin))) * this.ScreenToLocalBoxXf().Scale : 0;
     }
@@ -1403,7 +1416,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 } else if (!separated && node.isBlock) {
                     text += '\n';
                     separated = true;
-                } else if (node.type.name === 'hard_break') {
+                } else if (node.type.name === schema.nodes.hard_break.name) {
                     text += '\n';
                 }
             },
-- 
cgit v1.2.3-70-g09d2


From 6fde11fd2c1a1edc36adc463af526b6d33399d5b Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 16 Apr 2025 23:21:54 -0400
Subject: predictive text styling now enquires pre-existing styles + journal
 node automatically adds to all-day section of calendar view for easier access

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 31 +++++++++++++++++++---
 1 file changed, 27 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 797a9b812..39306632e 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -9,6 +9,7 @@ import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { Plugin } from 'prosemirror-state';
 import { RTFCast } from '../../../../fields/Types';
+import { Mark } from 'prosemirror-model';
 
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable journalDate: string;
@@ -17,6 +18,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     _ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
     predictiveTextRange: { from: number; to: number } | null = null; // where predictive text starts and ends
     private predictiveText: string | null = ' ... why?';
+    private prePredictiveMarks: Mark[] = [];
 
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(DailyJournal, fieldStr);
@@ -83,6 +85,14 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         console.log('Current text field:', this.dataDoc[this.fieldKey]);
     }
 
+    @action setInitialDateRange() {
+        if (!this.dataDoc.date_range) {
+            const isoDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
+            this.dataDoc.date_range = `${isoDate}|${isoDate}`;
+            this.dataDoc.allDay = true;
+        }
+    }
+
     /**
      * Tracks user typing text inout into the node, to call the insert predicted
      * text function when appropriate (i.e. when the user stops typing)
@@ -129,9 +139,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         // Only insert if we're at end of node, or there's a newline node after
         if (!isAtEndOfParent && !hasNewlineAfter) return;
 
-        const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
+        // Save current marks at cursor
+        const currentMarks = state.storedMarks || resolvedPos.marks();
+        this.prePredictiveMarks = [...currentMarks];
+
+        // color and italics are preset for predictive question, font and size are adaptive
         const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'lightgray' });
         const fontItalicsMark = schema.marks.em.create();
+        const fontSizeMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontSize');
+        const fontFamilyMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontFamily'); // if applicable
 
         this.predictiveText = ' ...'; // placeholder for now
 
@@ -142,10 +158,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
         // styled text node
         const text = ` ... ${res.trim()}`;
-        const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);
+        const predictedText = schema.text(text, [fontColorMark, fontItalicsMark, ...(fontSizeMark ? [fontSizeMark] : []), ...(fontFamilyMark ? [fontFamilyMark] : [])]);
 
         // Insert styled text at cursor position
-        const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks([state.schema.marks.pFontColor.create({ fontColor: 'gray' })]); // should probably instead inquire marks before predictive prompt
+        const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks(this.prePredictiveMarks);
+        // should probably instead inquire marks before predictive prompt
         dispatch(transaction);
 
         this.predictiveText = text;
@@ -172,11 +189,16 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                     const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' });
                                     const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' });
                                     tr.setStoredMarks([]);
-                                    tr.setStoredMarks([fontSizeMark, fontColorMark]);
+                                    if (this.prePredictiveMarks.length > 0) {
+                                        tr.setStoredMarks(this.prePredictiveMarks);
+                                    } else {
+                                        tr.setStoredMarks([fontSizeMark, fontColorMark]);
+                                    }
 
                                     dispatch(tr);
 
                                     this.predictiveText = null;
+                                    this.prePredictiveMarks = [];
                                     return false;
                                 }
                                 return true;
@@ -217,6 +239,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
             console.log('Journal title and text are default. Initializing...');
             this.setDailyTitle();
             this.setDailyText();
+            this.setInitialDateRange();
         } else {
             console.log('Journal already has content. Skipping initialization.');
         }
-- 
cgit v1.2.3-70-g09d2


From db2029602586985b7113fa436851b19746eac673 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 23 Apr 2025 15:18:36 -0400
Subject: added /ask feature so can trigger predictive question w/o waiting 3.5
 seconds

---
 .../views/nodes/formattedText/DailyJournal.tsx     | 40 ++++++++++++++++++----
 1 file changed, 34 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 39306632e..e4127fba3 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -85,11 +85,24 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         console.log('Current text field:', this.dataDoc[this.fieldKey]);
     }
 
+    /**
+     * Method to set initial date of document in the calendar view
+     */
+
     @action setInitialDateRange() {
-        if (!this.dataDoc.date_range) {
-            const isoDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
-            this.dataDoc.date_range = `${isoDate}|${isoDate}`;
-            this.dataDoc.allDay = true;
+        if (!this.dataDoc.date_range && this.journalDate) {
+            const parsedDate = new Date(this.journalDate);
+            if (!isNaN(parsedDate.getTime())) {
+                const localStart = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
+                const localEnd = new Date(localStart); // same day
+
+                this.dataDoc.date_range = `${localStart.toISOString()}|${localEnd.toISOString()}`;
+                this.dataDoc.allDay = true;
+
+                console.log('Set date_range and allDay on journal (from local date):', this.dataDoc.date_range);
+            } else {
+                console.log('Could not parse journalDate:', this.journalDate);
+            }
         }
     }
 
@@ -104,6 +117,22 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
         if (this.typingTimeout) clearTimeout(this.typingTimeout);
 
+        const { state } = editorView;
+        const cursorPos = state.selection.from;
+
+        // characters before cursor
+        const triggerText = state.doc.textBetween(Math.max(0, cursorPos - 4), cursorPos);
+
+        if (triggerText === '/ask') {
+            // remove /ask text
+            const tr = state.tr.delete(cursorPos - 4, cursorPos);
+            editorView.dispatch(tr);
+
+            // insert predicted question
+            this.insertPredictiveQuestion();
+            return;
+        }
+
         this.typingTimeout = setTimeout(() => {
             this.insertPredictiveQuestion();
         }, 3500);
@@ -149,7 +178,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const fontSizeMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontSize');
         const fontFamilyMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontFamily'); // if applicable
 
-        this.predictiveText = ' ...'; // placeholder for now
+        this.predictiveText = ' ...'; // placeholder
 
         const fullTextUpToCursor = state.doc.textBetween(0, state.selection.to, '\n', '\n');
         const gptPrompt = `Given the following incomplete journal entry, generate a single 2-5 word question that continues the user's thought:\n\n"${fullTextUpToCursor}"`;
@@ -162,7 +191,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
         // Insert styled text at cursor position
         const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks(this.prePredictiveMarks);
-        // should probably instead inquire marks before predictive prompt
         dispatch(transaction);
 
         this.predictiveText = text;
-- 
cgit v1.2.3-70-g09d2


From 78ac87b8acf63079071e5e8805692ed8c30042ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 23 Apr 2025 22:02:36 -0400
Subject: lots of typechecking fixes.

---
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  2 +-
 src/client/util/CalendarManager.tsx                |  2 +-
 src/client/util/DocumentManager.ts                 |  8 +++---
 src/client/util/LinkManager.ts                     |  3 +-
 src/client/util/ReplayMovements.ts                 |  6 ++--
 src/client/util/ScriptManager.ts                   |  9 ++----
 src/client/util/SearchUtil.ts                      |  5 ++--
 .../util/reportManager/ReportManagerComponents.tsx |  4 +--
 src/client/views/DashboardView.tsx                 | 28 +++++++++----------
 src/client/views/InkStrokeProperties.ts            |  8 +++---
 src/client/views/InkingStroke.tsx                  | 12 ++++----
 src/client/views/PropertiesButtons.tsx             | 23 ++++++++--------
 .../views/PropertiesDocBacklinksSelector.tsx       |  8 ++----
 src/client/views/PropertiesDocContextSelector.tsx  |  4 +--
 src/client/views/UndoStack.tsx                     |  5 +---
 src/client/views/animationtimeline/Region.tsx      | 32 ++++++++++------------
 src/client/views/animationtimeline/Track.tsx       | 24 ++++++++--------
 src/client/views/collections/CollectionMenu.tsx    |  4 +--
 .../collections/CollectionNoteTakingViewColumn.tsx |  2 +-
 .../views/collections/CollectionPileView.tsx       |  1 -
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  8 +++---
 .../views/collections/FlashcardPracticeUI.tsx      |  2 +-
 src/client/views/collections/TreeView.tsx          |  4 +--
 .../CollectionFreeFormLayoutEngines.tsx            |  2 +-
 .../collectionFreeForm/FaceCollectionBox.tsx       | 13 +++++----
 .../collectionSchema/SchemaColumnHeader.tsx        |  2 +-
 .../collectionSchema/SchemaTableCell.tsx           | 18 ++++++------
 src/client/views/linking/LinkMenuGroup.tsx         |  5 ++--
 src/client/views/linking/LinkMenuItem.tsx          |  4 +--
 .../views/newlightbox/ExploreView/ExploreView.tsx  |  4 +--
 src/client/views/newlightbox/NewLightboxView.tsx   |  7 ++---
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   | 16 +++++------
 .../views/nodes/DataVizBox/SchemaCSVPopUp.tsx      |  6 ++--
 .../nodes/DataVizBox/components/Histogram.tsx      | 17 ++++++------
 .../nodes/DataVizBox/components/LineChart.tsx      | 25 +++++++++++------
 .../views/nodes/DataVizBox/components/TableBox.tsx |  2 +-
 src/client/views/nodes/EquationBox.tsx             |  1 -
 src/client/views/nodes/FieldView.tsx               |  7 +++--
 src/client/views/nodes/FunctionPlotBox.tsx         | 11 ++++----
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 .../views/nodes/MapBox/DirectionsAnchorMenu.tsx    |  2 +-
 src/client/views/nodes/calendarBox/CalendarBox.tsx |  3 +-
 .../nodes/chatbot/chatboxcomponents/ChatBox.tsx    | 20 ++++++++------
 .../views/nodes/chatbot/tools/GetDocsTool.ts       |  5 +++-
 .../views/nodes/chatbot/vectorstore/Vectorstore.ts | 15 +++++-----
 .../views/nodes/formattedText/DailyJournal.tsx     |  2 +-
 src/client/views/nodes/scrapbook/ScrapbookBox.tsx  |  2 +-
 src/client/views/pdf/Annotation.tsx                |  7 +++--
 src/client/views/search/FaceRecognitionHandler.tsx |  6 ++--
 src/fields/Doc.ts                                  |  2 +-
 src/fields/Types.ts                                |  2 ++
 52 files changed, 207 insertions(+), 207 deletions(-)

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

diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 4b86a8341..15fd6313a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -311,7 +311,7 @@ export namespace GooglePhotos {
             sources
                 .filter(source => ImageCast(Doc.GetProto(source).data))
                 .forEach(async source => {
-                    const data = ImageCast(Doc.GetProto(source).data);
+                    const data = ImageCast(Doc.GetProto(source).data)!;
                     const url = data.url.href;
                     const target = Doc.MakeEmbedding(source);
                     const description = parseDescription(target, descriptionKey);
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index 4e321a893..b50e39c02 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -162,7 +162,7 @@ export class CalendarManager extends ObservableReactComponent<object> {
 
             console.log('my calendars: ', Doc.MyCalendars);
             if (this.creationType === 'new-calendar') {
-                Doc.AddDocToList(Doc.MyCalendars, 'data', calendar); // add to new calendar to dashboard calendars
+                Doc.MyCalendars && Doc.AddDocToList(Doc.MyCalendars, 'data', calendar); // add to new calendar to dashboard calendars
             }
         }
     };
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ad57c2a62..3bae2881e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -173,10 +173,10 @@ export class DocumentManager {
         while (
             containerDocContext.length &&
             DocCast(containerDocContext[0]?.embedContainer) &&
-            DocCast(containerDocContext[0].embedContainer)?._type_collection !== CollectionViewType.Docking &&
+            DocCast(containerDocContext[0].embedContainer)!._type_collection !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
-            containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext];
+            containerDocContext = [DocCast(containerDocContext[0].embedContainer)!, ...containerDocContext];
         }
         return containerDocContext;
     }
@@ -248,7 +248,7 @@ export class DocumentManager {
         finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
     ) => {
         const options = optionsIn;
-        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
+        Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
         const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
         if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
         let activatedTab = false;
@@ -272,7 +272,7 @@ export class DocumentManager {
             }));
         if (options.openLocation?.includes(OpenWhere.lightbox)) {
             // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
-            const target = DocCast(targetDoc.annotationOn, targetDoc);
+            const target = DocCast(targetDoc.annotationOn, targetDoc)!;
             const compView = this.getDocumentView(DocCast(target.embedContainer))?.ComponentView;
             if ((compView?.addDocTab ?? compView?._props.addDocTab)?.(target, options.openLocation)) {
                 await new Promise<void>(waitres => {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 344e2e4c0..d8e0c4cbe 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -217,7 +217,8 @@ export class LinkManager {
     }
 
     // finds the opposite anchor of a given anchor in a link
-    public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
+    public static getOppositeAnchor(linkDoc: Doc | undefined, anchor: Doc | undefined): Doc | undefined {
+        if (!linkDoc || !anchor) return undefined;
         const id = LinkManager.anchorIndex(linkDoc, anchor);
         const a1 = DocCast(linkDoc.link_anchor_1);
         const a2 = DocCast(linkDoc.link_anchor_2);
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index 62a09a8bc..4f0423342 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -108,9 +108,11 @@ export class ReplayMovements {
 
         movements.forEach((movement, i) => {
             if (typeof movement.doc === 'string') {
-                movements[i].doc = IdToDoc(movement.doc);
-                if (!movements[i].doc) {
+                const doc = IdToDoc(movement.doc);
+                if (!doc) {
                     console.log('ERROR: tracked doc not found');
+                } else {
+                    movements[i].doc = doc;
                 }
             }
         });
diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts
index 9158f6c0b..8c7f88bf6 100644
--- a/src/client/util/ScriptManager.ts
+++ b/src/client/util/ScriptManager.ts
@@ -1,7 +1,6 @@
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
-import { listSpec } from '../../fields/Schema';
-import { Cast, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
 import { Docs } from '../documents/Documents';
 import { ScriptingGlobals } from './ScriptingGlobals';
 
@@ -10,7 +9,6 @@ export class ScriptManager {
     // eslint-disable-next-line no-use-before-define
     private static _instance: ScriptManager;
     public static get Instance(): ScriptManager {
-        // eslint-disable-next-line no-return-assign
         return this._instance || (this._instance = new this());
     }
     private constructor() {
@@ -58,7 +56,7 @@ export class ScriptManager {
     public static addScriptToGlobals(scriptDoc: Doc): void {
         ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
 
-        const params = Cast(scriptDoc['data-params'], listSpec('string'), []);
+        const params = StrListCast(scriptDoc['data-params']);
         const paramNames = params.reduce((o: string, p: string) => {
             let out = o;
             if (params.indexOf(p) === params.length - 1) {
@@ -69,7 +67,6 @@ export class ScriptManager {
             return out;
         }, '' as string);
 
-        // eslint-disable-next-line no-new-func
         const f = new Function(paramNames, StrCast(scriptDoc.script));
 
         Object.defineProperty(f, 'name', { value: StrCast(scriptDoc.name), writable: false });
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index e4adcaa7e..fc3bb99ab 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -13,7 +13,7 @@ export namespace SearchUtil {
         const blockedKeys = matchKeyNames
             ? []
             : Object.entries(DocOptions)
-                  .filter(([, info]: [string, FInfo]) => !info?.searchable())
+                  .filter(([, info]: [string, FieldType | FInfo | undefined]) => (info instanceof FInfo ? !info.searchable() : true))
                   .map(([key]) => key);
 
         const exact = queryIn.startsWith('=');
@@ -22,8 +22,7 @@ export namespace SearchUtil {
         const results = new ObservableMap<Doc, string[]>();
         if (collectionDoc) {
             const docs = DocListCast(collectionDoc[Doc.LayoutDataKey(collectionDoc)]);
-            // eslint-disable-next-line @typescript-eslint/ban-types
-            const docIDs: String[] = [];
+            const docIDs: string[] = [];
             SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
                 const dtype = StrCast(doc.type) as DocumentType;
                 if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) {
diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx
index 92f877859..80653779e 100644
--- a/src/client/util/reportManager/ReportManagerComponents.tsx
+++ b/src/client/util/reportManager/ReportManagerComponents.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable react/require-default-props */
-/* eslint-disable prefer-destructuring */
 /* eslint-disable no-use-before-define */
 import * as React from 'react';
 import ReactMarkdown from 'react-markdown';
@@ -299,7 +297,7 @@ export function IssueView({ issue }: IssueViewProps) {
                     </div>
                 </div>
             )}
-            <ReactMarkdown className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
+            <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
                 {issueBody}
             </ReactMarkdown>
         </div>
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index f61f6db18..3ceb23ffd 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -113,12 +113,12 @@ export class DashboardView extends ObservableReactComponent<object> {
 
     getDashboards = (whichGroup: DashboardGroup) => {
         if (whichGroup === DashboardGroup.MyDashboards) {
-            return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard.$author === ClientUtils.CurrentUserEmail());
+            return DocListCast(Doc.MyDashboards?.data).filter(dashboard => dashboard.$author === ClientUtils.CurrentUserEmail());
         }
-        return DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc.dockingConfig);
+        return DocListCast(Doc.MySharedDocs?.data_dashboards).filter(doc => doc.dockingConfig);
     };
 
-    isUnviewedSharedDashboard = (dashboard: Doc) => !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard);
+    isUnviewedSharedDashboard = (dashboard: Doc) => !DocListCast(Doc.MySharedDocs?.viewed).includes(dashboard);
 
     @undoBatch
     createNewDashboard = (name: string, background?: string) => {
@@ -155,7 +155,7 @@ export class DashboardView extends ObservableReactComponent<object> {
     @action
     openNewDashboardModal = () => {
         this.openModal = true;
-        this.setNewDashboardName(`Dashboard ${DocListCast(Doc.MyDashboards.data).length + 1}`);
+        this.setNewDashboardName(`Dashboard ${DocListCast(Doc.MyDashboards?.data).length + 1}`);
     };
 
     _downX: number = 0;
@@ -191,7 +191,7 @@ export class DashboardView extends ObservableReactComponent<object> {
                         <Button
                             text={'Shared Dashboards (' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
                             active={this.selectedDashboardGroup === DashboardGroup.SharedDashboards}
-                            color={this.getDashboards(DashboardGroup.SharedDashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'green' : color}
+                            color={this.getDashboards(DashboardGroup.SharedDashboards).some(dash => !DocListCast(Doc.MySharedDocs?.viewed).includes(dash)) ? 'green' : color}
                             align="flex-start"
                             onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}
                             fillWidth
@@ -275,7 +275,7 @@ export class DashboardView extends ObservableReactComponent<object> {
     }
 
     public static openSharedDashboard = (dashboard: Doc) => {
-        Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
+        Doc.MySharedDocs && Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
         DashboardView.openDashboard(Doc.BestEmbedding(dashboard));
     };
 
@@ -283,8 +283,8 @@ export class DashboardView extends ObservableReactComponent<object> {
     /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
     public static openDashboard = (doc: Doc | undefined, fromHistory = false) => {
         if (!doc) return false;
-        Doc.AddDocToList(Doc.MyDashboards, 'data', doc);
-        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', doc);
+        Doc.MyDashboards && Doc.AddDocToList(Doc.MyDashboards, 'data', doc);
+        Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', doc);
 
         // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
         Doc.UserDoc() ? (Doc.ActiveDashboard = doc) : (Doc.GuestDashboard = doc);
@@ -316,11 +316,11 @@ export class DashboardView extends ObservableReactComponent<object> {
     };
 
     public static removeDashboard = (dashboard: Doc) => {
-        const dashboards = DocListCast(Doc.MyDashboards.data).filter(dash => dash !== dashboard);
+        const dashboards = DocListCast(Doc.MyDashboards?.data).filter(dash => dash !== dashboard);
         undoable(() => {
             if (dashboard === Doc.ActiveDashboard) DashboardView.openDashboard(dashboards.lastElement());
-            Doc.RemoveDocFromList(Doc.MyDashboards, 'data', dashboard);
-            Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashboard, undefined, true, true);
+            Doc.MyDashboards && Doc.RemoveDocFromList(Doc.MyDashboards, 'data', dashboard);
+            Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashboard, undefined, true, true);
             if (!dashboards.length) Doc.ActivePage = 'home';
         }, 'remove dashboard')();
     };
@@ -409,7 +409,7 @@ export class DashboardView extends ObservableReactComponent<object> {
     };
 
     public static createNewDashboard = (id?: string, name?: string, background?: string) => {
-        const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1;
+        const dashboardCount = DocListCast(Doc.MyDashboards?.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -425,8 +425,8 @@ export class DashboardView extends ObservableReactComponent<object> {
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
 
-        Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true);
-        Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
+        Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true);
+        Doc.MyDashboards && Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
         freeformDoc._embedContainer = dashboardDoc;
         dashboardDoc.$myPaneCount = 1;
         dashboardDoc.$myOverlayDocs = new List<Doc>();
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 425f5b6bb..41f38c008 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -99,7 +99,7 @@ export class InkStrokeProperties {
             controls.splice(i, 4, ...splicepts.map(p => ({ X: p.x, Y: p.y })));
 
             // Updating the indices of the control points whose handle tangency has been broken.
-            doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control)));
+            doc.brokenInkIndices = new List(NumListCast(doc.brokenInkIndices).map(control => (control > i ? control + 4 : control)));
             runInAction(() => {
                 this._currentPoint = -1;
             });
@@ -252,7 +252,7 @@ export class InkStrokeProperties {
             this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
                 const order = controlIndex % 4;
                 const closed = InkingStroke.IsClosed(ink);
-                const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []);
+                const brokenIndices = NumListCast(inkView.Document.brokenInkIndices);
                 if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
                     const cptBefore = ink[controlIndex];
                     const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY };
@@ -405,9 +405,9 @@ export class InkStrokeProperties {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const doc = view.Document;
             const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'), []);
-            const ind = brokenIndices.findIndex(value => value === controlIndex);
+            const ind = brokenIndices?.findIndex(value => value === controlIndex) ?? -1;
             if (ind !== -1) {
-                brokenIndices.splice(ind, 1);
+                brokenIndices!.splice(ind, 1);
                 const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]];
                 const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI);
                 const angleDifference = InkStrokeProperties.angleChange(handleB, oppositeHandleA, controlPoint);
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 0a49ea28e..0136f6abe 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -26,8 +26,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DashColor, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
 import { Doc } from '../../fields/Doc';
-import { InkData, InkField } from '../../fields/InkField';
-import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
+import { InkData } from '../../fields/InkField';
+import { BoolCast, InkCast, NumCast, RTFCast, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { Gestures } from '../../pen-gestures/GestureTypes';
 import { Docs } from '../documents/Documents';
@@ -35,6 +35,7 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { ContextMenu } from './ContextMenu';
 import { ViewBoxAnnotatableComponent } from './DocComponent';
 import { Colors } from './global/globalEnums';
@@ -42,14 +43,13 @@ import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
 import './InkStroke.scss';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { InkTangentHandles } from './InkTangentHandles';
+import { InkTranscription } from './InkTranscription';
+import { DocumentView } from './nodes/DocumentView';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox, FormattedTextBoxProps } from './nodes/formattedText/FormattedTextBox';
 import { PinDocView, PinProps } from './PinFuncs';
 import { StyleProp } from './StyleProp';
 import { ViewBoxInterface } from './ViewBoxInterface';
-import { InkTranscription } from './InkTranscription';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { DocumentView } from './nodes/DocumentView';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
@@ -245,7 +245,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * factor for converting between ink and screen space.
      */
     inkScaledData = () => {
-        const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null) ?? null)?.inkData ?? [];
+        const inkData = InkCast(this.dataDoc[this.fieldKey], InkCast(this.layoutDoc[this.fieldKey]) ?? null)?.inkData ?? [];
         const inkStrokeWidth = NumCast(this.layoutDoc.stroke_width, 1);
         const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2;
         const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2;
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 66936c64c..28566ba1d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -385,18 +385,19 @@ export class PropertiesButtons extends React.Component {
             const followLoc = this.selectedDoc._followLinkLocation;
             const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
 
-            let active = false;
-            // prettier-ignore
-            switch (value[0]) {
-                case 'linkInPlace': active = linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView();  break;
-                case 'linkOnRight': active = linkButton && followLoc === OpenWhere.addRight; break;
-                case 'enterPortal': active = linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break;
-                case 'toggleDetail':active = ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break;
-                case 'nothing':     active = !linkButton && this.selectedDoc.onClick === undefined;break;
-                default:
-            }
+            const active = () => {
+                // prettier-ignore
+                switch (value[0]) {
+                    case 'linkInPlace': return linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView();  break;
+                    case 'linkOnRight': return linkButton && followLoc === OpenWhere.addRight; break;
+                    case 'enterPortal': return linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break;
+                    case 'toggleDetail':return ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break;
+                    case 'nothing':    return !linkButton && this.selectedDoc.onClick === undefined;break;
+                    default: return false;
+                }
+            };
             return (
-                <div className="list-item" key={`${value}`} style={{ backgroundColor: active ? Colors.LIGHT_BLUE : undefined }} onClick={click}>
+                <div className="list-item" key={`${value}`} style={{ backgroundColor: active() ? Colors.LIGHT_BLUE : undefined }} onClick={click}>
                     {value[1]}
                 </div>
             );
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index e30d14eae..5a00e6a89 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -1,18 +1,16 @@
-/* eslint-disable react/require-default-props */
-/* eslint-disable react/no-unused-prop-types */
 import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
-import { Cast } from '../../fields/Types';
+import { DocCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
 import { LinkManager } from '../util/LinkManager';
 import { SettingsManager } from '../util/SettingsManager';
 import './PropertiesDocBacklinksSelector.scss';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { LinkMenu } from './linking/LinkMenu';
-import { OpenWhere } from './nodes/OpenWhere';
 import { DocumentView } from './nodes/DocumentView';
+import { OpenWhere } from './nodes/OpenWhere';
 
 type PropertiesDocBacklinksSelectorProps = {
     Document: Doc;
@@ -26,7 +24,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
     getOnClick = action((link: Doc) => {
         const linkAnchor = this.props.Document;
         const other = Doc.getOppositeAnchor(link, linkAnchor);
-        const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other;
+        const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? DocCast(other.annotationOn) : other;
         LinkManager.Instance.currentLink = link;
         if (otherdoc) {
             otherdoc.hidden = false;
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index ee6486a9c..1aa956e16 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
-import { Cast, StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import './PropertiesDocContextSelector.scss';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -29,7 +29,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
         const target = this._props.DocView.Document;
         const targetContext = this._props.DocView.containerViewPath?.().lastElement()?.Document;
         const embeddings = Doc.GetEmbeddings(target);
-        const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
+        const containerProtos = embeddings.filter(embedding => DocCast(embedding.embedContainer)).reduce((set, embedding) => set.add(DocCast(embedding.embedContainer)!), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => (Doc.GetEmbeddings(container).length ? Doc.GetEmbeddings(container) : [container]));
         const containers = containerSets.reduce((p, set) => {
             set.map(s => p.add(s));
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 32b97b31a..067020a62 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -7,9 +7,8 @@ import { SettingsManager } from '../util/SettingsManager';
 import { UndoManager } from '../util/UndoManager';
 import './UndoStack.scss';
 
-interface UndoStackProps {}
 @observer
-export class UndoStack extends React.Component<UndoStackProps> {
+export class UndoStack extends React.Component<object> {
     render() {
         const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.userVariantColor;
         const color = UndoManager.batchCounter.get() ? 'black' : SettingsManager.userColor;
@@ -34,7 +33,6 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                     {Array.from(UndoManager.undoStackNames).map((name, i) => (
                                         <div
                                             className="undoStack-resultContainer"
-                                            // eslint-disable-next-line react/no-array-index-key
                                             key={i}
                                             onClick={() => {
                                                 const size = UndoManager.undoStackNames.length;
@@ -48,7 +46,6 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                         .map((name, i) => (
                                             <div
                                                 className="undoStack-resultContainer"
-                                                // eslint-disable-next-line react/no-array-index-key
                                                 key={i}
                                                 onClick={() => {
                                                     for (let n = 0; n <= i; n++) UndoManager.Redo();
diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx
index 99163f6c6..40fcb2bf6 100644
--- a/src/client/views/animationtimeline/Region.tsx
+++ b/src/client/views/animationtimeline/Region.tsx
@@ -12,6 +12,18 @@ import './Region.scss';
 import './Timeline.scss';
 import { TimelineMenu } from './TimelineMenu';
 
+export const RegionDataSchema = createSchema({
+    position: defaultSpec('number', 0),
+    duration: defaultSpec('number', 0),
+    keyframes: listSpec(Doc),
+    fadeIn: defaultSpec('number', 0),
+    fadeOut: defaultSpec('number', 0),
+    functions: listSpec(Doc),
+    hasData: defaultSpec('boolean', false),
+});
+export type RegionData = makeInterface<[typeof RegionDataSchema]>;
+export const RegionData = makeInterface(RegionDataSchema);
+
 /**
  * Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also
  */
@@ -108,18 +120,6 @@ export namespace RegionHelpers {
     };
 }
 
-export const RegionDataSchema = createSchema({
-    position: defaultSpec('number', 0),
-    duration: defaultSpec('number', 0),
-    keyframes: listSpec(Doc),
-    fadeIn: defaultSpec('number', 0),
-    fadeOut: defaultSpec('number', 0),
-    functions: listSpec(Doc),
-    hasData: defaultSpec('boolean', false),
-});
-export type RegionData = makeInterface<[typeof RegionDataSchema]>;
-export const RegionData = makeInterface(RegionDataSchema);
-
 interface IProps {
     animatedDoc: Doc;
     RegionData: Doc;
@@ -184,7 +184,7 @@ export class Region extends ObservableReactComponent<IProps> {
         return RegionHelpers.convertPixelTime(this.regiondata.fadeOut, 'mili', 'pixel', this._props.tickSpacing, this._props.tickIncrement);
     }
 
-    constructor(props: any) {
+    constructor(props: IProps) {
         super(props);
         makeObservable(this);
     }
@@ -220,9 +220,7 @@ export class Region extends ObservableReactComponent<IProps> {
             }, 200);
             this._doubleClickEnabled = true;
             document.addEventListener('pointermove', this.onBarPointerMove);
-            document.addEventListener('pointerup', (e: PointerEvent) => {
-                document.removeEventListener('pointermove', this.onBarPointerMove);
-            });
+            document.addEventListener('pointerup', () => document.removeEventListener('pointermove', this.onBarPointerMove));
         }
     };
 
@@ -426,6 +424,7 @@ export class Region extends ObservableReactComponent<IProps> {
                         .map(region => ({ pos: NumCast(region.position), dur: NumCast(region.duration) }))
                         .forEach(({ pos, dur }) => {
                             if (pos !== this.regiondata.position) {
+                                // eslint-disable-next-line no-param-reassign
                                 val += this.regiondata.position;
                                 if (val < 0 || (val > pos && val < pos + dur)) {
                                     cannotMove = true;
@@ -483,7 +482,6 @@ export class Region extends ObservableReactComponent<IProps> {
      * this probably needs biggest change, since everyone expected all keyframes to have a circle (and draggable)
      */
     drawKeyframes = () => {
-        const keyframeDivs: JSX.Element[] = [];
         return DocListCast(this.regiondata.keyframes).map(kf => {
             return (
                 <>
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 1e4ed74be..dea7b6aae 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,4 +1,4 @@
-import { action, computed, intercept, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { action, computed, intercept, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc';
@@ -6,7 +6,7 @@ import { Copy } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { listSpec } from '../../../fields/Schema';
-import { Cast, NumCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../../fields/Types';
 import { Transform } from '../../util/Transform';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { Region, RegionData, RegionHelpers } from './Region';
@@ -29,16 +29,16 @@ interface IProps {
 @observer
 export class Track extends ObservableReactComponent<IProps> {
     @observable private _inner = React.createRef<HTMLDivElement>();
-    @observable private _currentBarXReaction: any = undefined;
-    @observable private _timelineVisibleReaction: any = undefined;
-    @observable private _autoKfReaction: any = undefined;
+    @observable private _currentBarXReaction: IReactionDisposer | undefined = undefined;
+    @observable private _timelineVisibleReaction: IReactionDisposer | undefined = undefined;
+    @observable private _autoKfReaction: IReactionDisposer | undefined = undefined;
     @observable private _newKeyframe: boolean = false;
     private readonly MAX_TITLE_HEIGHT = 75;
     @observable private _trackHeight = 0;
     private primitiveWhitelist = ['x', 'y', '_freeform_panX', '_freeform_panY', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop'];
     private objectWhitelist = ['data'];
 
-    constructor(props: any) {
+    constructor(props: IProps) {
         super(props);
         makeObservable(this);
     }
@@ -101,11 +101,11 @@ export class Track extends ObservableReactComponent<IProps> {
         }
         const keyframes = Cast(this.saveStateRegion.keyframes, listSpec(Doc)) as List<Doc>;
         const kfIndex = keyframes.indexOf(this.saveStateKf);
-        const kf = keyframes[kfIndex] as Doc; //index in the keyframe
+        const kf = DocCast(keyframes[kfIndex]); //index in the keyframe
         if (this._newKeyframe) {
-            DocListCast(this.saveStateRegion.keyframes).forEach((kf, index) => {
-                this.copyDocDataToKeyFrame(kf);
-                kf.opacity = index === 0 || index === 3 ? 0.1 : 1;
+            DocListCast(this.saveStateRegion.keyframes).forEach((keyF, index) => {
+                this.copyDocDataToKeyFrame(keyF);
+                keyF.opacity = index === 0 || index === 3 ? 0.1 : 1;
             });
             this._newKeyframe = false;
         }
@@ -144,7 +144,7 @@ export class Track extends ObservableReactComponent<IProps> {
             () => {
                 return [...this.primitiveWhitelist.map(key => this._props.animatedDoc[key]), ...objects];
             },
-            (changed, reaction) => {
+            (/* changed, reaction */) => {
                 //check for region
                 const region = this.findRegion(this.time);
                 if (region !== undefined) {
@@ -374,7 +374,7 @@ export class Track extends ObservableReactComponent<IProps> {
 
     @action
     copyDocDataToKeyFrame = (doc: Doc) => {
-        var somethingChanged = false;
+        let somethingChanged = false;
         this.primitiveWhitelist.map(key => {
             const originalVal = this._props.animatedDoc[key];
             somethingChanged = somethingChanged || originalVal !== doc[key];
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 1576a8e4a..7f835938b 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -46,7 +46,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
         makeObservable(this);
         CollectionMenu.Instance = this;
         this._canFade = false; // don't let the inking menu fade away
-        this.Pinned = Cast(Doc.UserDoc().menuCollections_pinned, 'boolean', true);
+        this.Pinned = BoolCast(Doc.UserDoc().menuCollections_pinned, true);
         this.jumpTo(300, 300);
     }
 
@@ -269,7 +269,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         initialize: (button: Doc) => {
             const activeDash = Doc.ActiveDashboard;
             if (activeDash) {
-                button.target_childFilters = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as ObjectField) : undefined;
+                button.target_childFilters = (Doc.MySearcher?._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher?._childFilters || activeDash._childFilters) as ObjectField) : undefined;
                 button.target_searchFilterDocs = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs) : undefined;
             }
         },
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index f283b0abe..461689a70 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -155,7 +155,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
     // all docs are added to the column directly to the left.
     @undoBatch
     deleteColumn = () => {
-        const colHdrData = Array.from(Cast(this._props.Doc[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this._props.Doc[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), [])!);
         if (this._props.headingObject) {
             //          this._props.docList.forEach(d => (d['$'+this._props.pivotField] = undefined));
             colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index eea128803..5a85c8ee3 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -67,7 +67,6 @@ export class CollectionPileView extends CollectionSubView() {
         return (
             <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
                 <CollectionFreeFormView
-                    // eslint-disable-next-line react/jsx-props-no-spreading
                     {...this._props} //
                     layoutEngine={this.layoutEngine}
                     addDocument={this.addPileDoc}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index fd4bdf364..4e7e19548 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -435,7 +435,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         anchor['$' + endTag] = anchorEndTime;
         if (addAsAnnotation) {
             if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
-                Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
+                Cast(dataDoc[fieldKey], listSpec(Doc), [])!.push(anchor);
             } else {
                 dataDoc[fieldKey] = new List<Doc>([anchor]);
             }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3497a62cb..bee5d016d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         if (res && de.complete.docDragData) {
             if (this.Document !== Doc.MyRecentlyClosed)
                 de.complete.docDragData.droppedDocuments.forEach(doc => {
-                    if (this.Document !== Doc.MyRecentlyClosed) Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
+                    if (this.Document !== Doc.MyRecentlyClosed) Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
                 });
         }
         return res;
@@ -193,7 +193,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         res &&
             doclist.forEach(doc => {
                 Doc.SetContainer(doc, this.Document);
-                if (this.Document !== Doc.MyRecentlyClosed) Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
+                if (this.Document !== Doc.MyRecentlyClosed) Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
             });
         return res;
     };
@@ -273,8 +273,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         );
     }
     childContextMenuItems = () => {
-        const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
-        const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+        const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), [])!;
+        const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), [])!;
         const icons = StrListCast(this.Document.childContextMenuIcons);
         return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     };
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index f24a8acb7..2f46c00bd 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -60,7 +60,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
 
     get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
 
-    @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
+    @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns?.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
     @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
 
     btnHeight = () => NumCast(this.filterDoc?.height);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4beb75074..fb2d0955f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -70,7 +70,7 @@ export interface TreeViewProps {
     treeViewHideHeaderFields: () => boolean;
     renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
     onCheckedClick?: () => ScriptField;
-    onChildClick?: () => ScriptField;
+    onChildClick?: () => ScriptField | undefined;
     skipFields?: string[];
     firstLevel: boolean;
     // TODO: [AL] add these
@@ -1268,7 +1268,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         treeViewHideHeaderFields: () => boolean,
         renderedIds: string[],
         onCheckedClick: undefined | (() => ScriptField),
-        onChildClick: undefined | (() => ScriptField),
+        onChildClick: undefined | (() => ScriptField | undefined),
         skipFields: string[] | undefined,
         firstLevel: boolean,
         whenChildContentsActiveChanged: (isActive: boolean) => void,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 158bac7ba..4d17dedfb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -108,7 +108,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
 }
 
 function toNumber(val: FieldResult<FieldType>) {
-    return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getTime() : NumCast(val, Number(StrCast(val)));
+    return val === undefined ? undefined : DateCast(val) ? DateCast(val)!.date.getTime() : NumCast(val, Number(StrCast(val)));
 }
 
 export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 72485aa86..ad05a798b 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -106,7 +106,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
                     // assign the face in the image that's closest to the face collection's face
                     if (faceAnno) {
-                        faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face));
+                        DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face)!);
                         FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
                         faceAnno.$face = this.Document[DocData];
                     }
@@ -116,7 +116,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
             ?.filter(doc => DocCast(doc.face)?.type === DocumentType.UFACE)
             .forEach(faceAnno => {
                 const imgDoc = faceAnno;
-                faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face));
+                DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face)!);
                 FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
                 faceAnno.$face = this.Document[DocData];
             });
@@ -187,7 +187,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                         })}>
                         {FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
-                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
+                            const [name, type] = ImageCastToNameType(doc?.[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
                             return (
                                 <div
                                     className="image-wrapper"
@@ -197,7 +197,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             this,
                                             e,
                                             () => {
-                                                const dragDoc = DocListCast(doc.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
+                                                const dragDoc = DocListCast(doc?.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
                                                 DragManager.StartDocumentDrag([e.target as HTMLElement], new DragManager.DocumentDragData([dragDoc], dropActionType.embed), e.clientX, e.clientY);
                                                 return true;
                                             },
@@ -205,7 +205,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             emptyFunction
                                         )
                                     }>
-                                    <img onClick={() => DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
+                                    <img onClick={() => doc && DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
                                     <div className="remove-item">
                                         <IconButton tooltip={'Remove Doc From Face Collection'} onPointerDown={() => this.removeFaceImageFromUniqueFace(doc)} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
                                     </div>
@@ -239,11 +239,12 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     addDocument = (doc: Doc | Doc[], annotationKey?: string) => {
         const uniqueFaceDoc = doc instanceof Doc ? doc : doc[0];
         const added = uniqueFaceDoc.type === DocumentType.UFACE;
         if (added) {
-            Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
+            Doc.MyFaceCollection && Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
             Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
         }
         return added;
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 81a2d8e64..16d33eb93 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -107,7 +107,7 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea
             focus: emptyFunction,
             addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
-            Document: DocCast(Document.rootDocument, Document),
+            Document: DocCast(Document.rootDocument, Document)!,
             fieldKey: fieldKey,
             PanelWidth: columnWidth,
             PanelHeight: props.rowHeight,
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 8e1edc1ee..8b34b4139 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -106,7 +106,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
         let protoCount = 0;
         const layoutDoc = fieldKey.startsWith('_') ? Document[DocLayout] : Document;
-        let doc = Document;
+        let doc: Doc | undefined = Document;
         while (doc) {
             if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break;
             protoCount++;
@@ -168,9 +168,11 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         while ((matches = idPattern.exec(field)) !== null) {
             results.push([matches[0], matches[1].replace(/"/g, '')]);
         }
-        results.forEach(idFuncPair => {
-            modField = modField.replace(idFuncPair[0], 'd' + DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1])).toString());
-        });
+        results
+            .filter(idFuncPair => IdToDoc(idFuncPair[1]))
+            .forEach(idFuncPair => {
+                modField = modField.replace(idFuncPair[0], 'd' + DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1])!).toString());
+            });
 
         if (modField.endsWith(';')) modField = modField.substring(0, modField.length - 1);
 
@@ -328,7 +330,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
         const altpaths = alts
             .map(doc => Cast(doc[Doc.LayoutDataKey(doc)], ImageField, null)?.url)
             .filter(url => url)
-            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+            .map(url => this.choosePath(url!)); // access the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
         // If there is a path, follow it; otherwise, follow a link to a default image icon
         const url = paths.length ? paths : [ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
@@ -379,7 +381,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
     }
 
     @observable _pickingDate: boolean = false;
-    @computed get date(): DateField {
+    @computed get date(): DateField | undefined {
         // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
         return DateCast(this._props.Doc[this._props.fieldKey]);
     }
@@ -399,7 +401,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
         return (
             <>
                 <div style={{ pointerEvents: 'none' }} tabIndex={1}>
-                    <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} />
+                    <DatePicker dateFormat="Pp" selected={this.date?.date ?? new Date()} onChange={emptyFunction} />
                 </div>
                 {pointerEvents === 'none' || !selectedCell(this._props) ? null : (
                     <Popup
@@ -410,7 +412,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
                         background={SnappingManager.userBackgroundColor}
                         popup={
                             <div style={{ width: 'fit-content', height: '200px' }}>
-                                <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
+                                <DatePicker open dateFormat="Pp" selected={this.date?.date ?? new Date()} onChange={this.handleChange} />
                             </div>
                         }
                     />
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index c15508669..9eaf0e491 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,10 +1,9 @@
-/* eslint-disable react/require-default-props */
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { Cast, DocCast } from '../../../fields/Types';
+import { DocCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentView } from '../nodes/DocumentView';
 import './LinkMenu.scss';
@@ -56,7 +55,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                   ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1')
                       ? DocCast(linkDoc.link_anchor_2)
                       : DocCast(linkDoc.link_anchor_1)
-                  : Doc.getOppositeAnchor(linkDoc, sourceDoc) || Doc.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null)?.annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
+                  : Doc.getOppositeAnchor(linkDoc, sourceDoc) || Doc.getOppositeAnchor(linkDoc, DocCast(linkDoc.link_anchor_2)?.annotationOn === sourceDoc ? DocCast(linkDoc.link_anchor_2) : DocCast(linkDoc.link_anchor_1));
             return !destDoc || !sourceDoc ? null : (
                 <LinkMenuItem
                     key={linkDoc[Id]}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 1c92c3b0d..c984a7053 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -68,8 +68,8 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
     @computed get sourceAnchor() {
         const ldoc = this._props.linkDoc;
         if (this._props.sourceDoc !== ldoc.link_anchor_1 && this._props.sourceDoc !== ldoc.link_anchor_2) {
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1)?.annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2)?.annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
         }
         return this._props.sourceDoc;
     }
diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
index f8c07cc43..7f9a13549 100644
--- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx
+++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
 import * as React from 'react';
 import { StrCast } from '../../../../fields/Types';
 import { NewLightboxView } from '../NewLightboxView';
@@ -19,7 +17,7 @@ export function ExploreView(props: IExploreView) {
                         const x = (rec.embedding.x / xBound) * 50;
                         const y = (rec.embedding.y / yBound) * 50;
                         return (
-                            <div className="exploreView-doc" onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
+                            <div key={'' + x + ' ' + y} className="exploreView-doc" onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
                                 {rec.title}
                             </div>
                         );
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index 87cef01d2..7ec45d0dd 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -5,7 +5,7 @@ import { returnEmptyFilter, returnTrue } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { CreateLinkToActiveAudio, Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
-import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { GestureOverlay } from '../GestureOverlay';
@@ -100,7 +100,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
             SnappingManager.SetExploreMode(false);
         } else {
             const l = CreateLinkToActiveAudio(() => doc).lastElement();
-            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
+            DocCast(l?.link_anchor_2) && (DocCast(l!.link_anchor_2)!.backgroundColor = 'lightgreen');
             DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.());
             // DocumentView.PinDoc(doc, { hidePresBox: true });
             this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
@@ -142,7 +142,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
         const targetDocView = target && DocumentView.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
-            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
+            DocCast(l?.link_anchor_2) && (DocCast(l!.link_anchor_2)!.backgroundColor = 'lightgreen');
             DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 });
             if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target });
         } else if (!target && NewLightboxView.path.length) {
@@ -326,7 +326,6 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
 }
 interface NewLightboxTourBtnProps {
     navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element;
-    // eslint-disable-next-line react/no-unused-prop-types
     future: () => Opt<Doc[]>;
     stepInto: () => void;
 }
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index c80e8ebc1..9369ff98a 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -108,7 +108,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // all CSV records in the dataset (that aren't an empty row)
     @computed.struct get records() {
         try {
-            const records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
+            const records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey])?.url.href ?? '');
             this._urlError = false;
             return records?.filter(record => Object.keys(record).some(key => record[key])) ?? [];
         } catch {
@@ -344,7 +344,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     componentDidMount() {
         this._props.setContentViewBox?.(this);
         if (!this._urlError) {
-            if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData();
+            if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey])?.url.href ?? '')) this.fetchData();
         }
         this._disposers.datavis = reaction(
             () => {
@@ -386,8 +386,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     this.layoutDoc._dataViz_schemaOG = loading;
                 }
                 const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc;
-                const ogHref = CsvCast(ogDoc[this.fieldKey]) ? CsvCast(ogDoc[this.fieldKey]).url.href : undefined;
-                const { href } = CsvCast(this.Document[this.fieldKey]).url;
+                const ogHref = CsvCast(ogDoc[this.fieldKey]) ? CsvCast(ogDoc[this.fieldKey])!.url.href : undefined;
+                const { href } = CsvCast(this.Document[this.fieldKey])?.url ?? { href: '' };
                 if (ogHref && !DataVizBox.datasetSchemaOG.has(href)) {
                     // sets original dataset to the var
                     const lastRow = current.pop();
@@ -399,7 +399,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             },
             current => {
                 if (current) {
-                    const { href } = CsvCast(this.Document[this.fieldKey]).url;
+                    const { href } = CsvCast(this.Document[this.fieldKey])?.url ?? { href: '' };
                     if (this.layoutDoc.dataViz_schemaLive) DataVizBox.dataset.set(href, current);
                     else DataVizBox.dataset.set(href, DataVizBox.datasetSchemaOG.get(href)!);
                 }
@@ -414,9 +414,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     fetchData = () => {
         if (!this.Document.dataViz_asSchema) {
-            DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
+            DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey])?.url.href ?? '', []); // assign temporary dataset as a lock to prevent duplicate server requests
             fetch('/csvData?uri=' + (this.dataUrl?.url.href ?? '')) //
-                .then(res => res.json().then(action(jsonRes => !jsonRes.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, jsonRes))));
+                .then(res => res.json().then(action(jsonRes => !jsonRes.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey])?.url.href ?? '', jsonRes))));
         }
     };
 
@@ -523,7 +523,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc;
         GPTPopup.Instance.setDataJson('');
         GPTPopup.Instance.setMode(GPTPopupMode.DATA);
-        const csvdata = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
+        const csvdata = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey])?.url.href ?? '');
         GPTPopup.Instance.setDataJson(JSON.stringify(csvdata));
         GPTPopup.Instance.generateDataAnalysis();
     });
diff --git a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
index 8ae29a88c..1e2a95d31 100644
--- a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
+++ b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
@@ -11,10 +11,8 @@ import { DragManager } from '../../../util/DragManager';
 import { DocumentView } from '../DocumentView';
 import './SchemaCSVPopUp.scss';
 
-interface SchemaCSVPopUpProps {}
-
 @observer
-export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
+export class SchemaCSVPopUp extends React.Component<object> {
     // eslint-disable-next-line no-use-before-define
     static Instance: SchemaCSVPopUp;
 
@@ -23,7 +21,7 @@ export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
     @observable public target: Doc | undefined = undefined;
     @observable public visible: boolean = false;
 
-    constructor(props: SchemaCSVPopUpProps) {
+    constructor(props: object) {
         super(props);
         makeObservable(this);
         SchemaCSVPopUp.Instance = this;
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index 5450d03b1..a7c4a00b0 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -5,10 +5,9 @@ import { IReactionDisposer, action, computed, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaFillDrip } from 'react-icons/fa';
-import { Doc, NumListCast } from '../../../../../fields/Doc';
+import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc';
 import { List } from '../../../../../fields/List';
-import { listSpec } from '../../../../../fields/Schema';
-import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { DocCast, StrCast } from '../../../../../fields/Types';
 import { Docs } from '../../../../documents/Documents';
 import { undoable } from '../../../../util/UndoManager';
 import { ObservableReactComponent } from '../../../ObservableReactComponent';
@@ -108,13 +107,13 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
     }
 
     @computed get defaultBarColor() {
-        return Cast(this.layoutDoc.dataViz_histogram_defaultColor, 'string', '#69b3a2');
+        return StrCast(this.layoutDoc.dataViz_histogram_defaultColor, '#69b3a2')!;
     }
     @computed get barColors() {
-        return Cast(this.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
+        return StrListCast(this.layoutDoc.dataViz_histogram_barColors);
     }
     @computed get selectedBins() {
-        return Cast(this.layoutDoc.dataViz_histogram_selectedBins, listSpec('number'), null);
+        return NumListCast(this.layoutDoc.dataViz_histogram_selectedBins);
     }
 
     @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
@@ -129,7 +128,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
         // restore selected bars
         this._histogramSvg?.selectAll('rect').attr('class', dIn => {
             const d = dIn as HistogramData;
-            if (this.selectedBins.some(selBin => d[0] === selBin)) {
+            if (this.selectedBins?.some(selBin => d[0] === selBin)) {
                 this._selectedBars.push(d);
                 return 'histogram-bar hover';
             }
@@ -199,10 +198,10 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
                     const alreadySelected = this._selectedBars.findIndex(eachData => !Object.keys(d).some(key => d[key] !== eachData[key]));
                     if (alreadySelected !== -1) {
                         this._selectedBars.splice(alreadySelected, 1);
-                        this.selectedBins.splice(alreadySelected, 1);
+                        this.selectedBins?.splice(alreadySelected, 1);
                     } else {
                         this._selectedBars.push(d);
-                        this.selectedBins.push(d[0] as number);
+                        this.selectedBins?.push(d[0] as number);
                     }
                     const showSelectedLabel = (dataset: HistogramData[]) => {
                         const datum = dataset.lastElement();
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 6b047546c..80fadf178 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -26,7 +26,7 @@ export interface LineChartProps {
     layoutDoc: Doc;
     axes: string[];
     titleCol: string;
-    records: { [key: string]: any }[];
+    records: { [key: string]: string }[];
     width: number;
     height: number;
     dataDoc: Doc;
@@ -47,7 +47,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
     @observable _currSelected: DataPoint | undefined = undefined;
 
     // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
-    constructor(props: any) {
+    constructor(props: LineChartProps) {
         super(props);
         makeObservable(this);
     }
@@ -79,7 +79,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
     @computed get incomingHighlited() {
         // return selected x and y axes
         // otherwise, use the selection of whatever is linked to us
-        const incomingVizBox = DocumentView.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
+        const incomingVizBox = this.parentViz && (DocumentView.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox);
         const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows);
         return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor
     }
@@ -170,8 +170,8 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
             }
         });
         if (!ptWasSelected) {
-            selectedDatapoints.push(d.x + ',' + d.y);
-            this._currSelected = selectedDatapoints.length > 1 ? undefined : d;
+            selectedDatapoints?.push(d.x + ',' + d.y);
+            this._currSelected = (selectedDatapoints?.length ?? 0 > 1) ? undefined : d;
         }
 
         // for filtering child dataviz docs
@@ -190,7 +190,14 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
         }
     }
 
-    drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>, higlightFocusPt: any, tooltip: any) {
+    drawDataPoints(
+        data: DataPoint[], //
+        idx: number,
+        xScale: d3.ScaleLinear<number, number, never>,
+        yScale: d3.ScaleLinear<number, number, never>,
+        higlightFocusPt: d3.Selection<SVGGElement, unknown, null, undefined>,
+        tooltip: d3.Selection<HTMLDivElement, unknown, null, undefined>
+    ) {
         if (this._lineChartSvg) {
             const circleClass = '.circle-' + idx;
             this._lineChartSvg
@@ -211,7 +218,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
                 .on('mouseleave', () => {
                     tooltip?.transition().duration(300).style('opacity', 0);
                 })
-                .on('click', (e: any) => {
+                .on('click', e => {
                     const d0 = { x: Number(e.target.getAttribute('data-x')), y: Number(e.target.getAttribute('data-y')) };
                     // find .circle-d1 with data-x = d0.x and data-y = d0.y
                     this.setCurrSelected(d0);
@@ -220,7 +227,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
         }
     }
 
-    drawChart = (dataSet: any[][], rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number }, width: number, height: number) => {
+    drawChart = (dataSet: { x: number; y: number }[][], rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number }, width: number, height: number) => {
         // clearing tooltip and the current chart
         d3.select(this._lineChartRef).select('svg').remove();
         d3.select(this._lineChartRef).select('.tooltip').remove();
@@ -277,7 +284,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
             svg.append('path').attr('stroke', 'red');
 
             // legend
-            const color: any = d3.scaleOrdinal().range(['black', 'blue']).domain([this._props.axes[1], this._props.axes[2]]);
+            const color = d3.scaleOrdinal().range(['black', 'blue']).domain([this._props.axes[1], this._props.axes[2]]);
             svg.selectAll('mydots')
                 .data([this._props.axes[1], this._props.axes[2]])
                 .enter()
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index b73123691..ad2731109 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -128,7 +128,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
             selected.splice(selected.indexOf(rowId), 1);
         } else selected?.push(rowId);
         e.stopPropagation();
-        this.hasRowsToFilter = selected.length > 0;
+        this.hasRowsToFilter = (selected?.length ?? 0) > 0;
     };
 
     columnPointerDown = (e: React.PointerEvent, col: string) => {
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index dcc6e27ed..3cacb6692 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,7 +1,6 @@
 import { action, computed, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { DocUtils } from '../../documents/DocUtils';
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 34e3a5d76..a6872f8dc 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -6,16 +6,17 @@ import { DateField } from '../../../fields/DateField';
 import { Doc, Field, FieldType, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
+import { WebField } from '../../../fields/URLField';
+import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { Transform } from '../../util/Transform';
+import { ContextMenuProps } from '../ContextMenuItem';
 import { PinProps } from '../PinFuncs';
 import { ViewBoxInterface } from '../ViewBoxInterface';
 import { DocumentView } from './DocumentView';
 import { FocusViewOptions } from './FocusViewOptions';
-import { OpenWhere } from './OpenWhere';
-import { WebField } from '../../../fields/URLField';
-import { ContextMenuProps } from '../ContextMenuItem';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { OpenWhere } from './OpenWhere';
 
 export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>;
 export type StyleProviderFuncType = (
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 91c351895..8e4b64851 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -2,10 +2,11 @@ import functionPlot, { Chart } from 'function-plot';
 import { action, computed, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
+import { Doc, DocListCast, NumListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
-import { listSpec } from '../../../fields/Schema';
-import { Cast, StrCast } from '../../../fields/Types';
+import { StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { DocUtils } from '../../documents/DocUtils';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -15,8 +16,6 @@ import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { PinDocView, PinProps } from '../PinFuncs';
 import { FieldView, FieldViewProps } from './FieldView';
-import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
-import { emptyFunction } from '../../../Utils';
 
 @observer
 export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -76,7 +75,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         this._plotEle = ele || this._plotEle;
         const width = this._props.PanelWidth();
         const height = this._props.PanelHeight();
-        const xrange = Cast(this.layoutDoc.xRange, listSpec('number'), [-10, 10]);
+        const xrange = NumListCast(this.layoutDoc.xRange, [-10, 10])!;
         try {
             this._plotEle?.children.length && this._plotEle.removeChild(this._plotEle.children[0]);
             this._plot = functionPlot({
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9c6b8e99c..999d7089b 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -212,7 +212,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     drop = undoable(
         action((e: Event, de: DragManager.DropEvent) => {
-            if (de.complete.docDragData && this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, this.DocumentView?.())) {
+            if (de.complete.docDragData && this._props.rejectDrop?.(de, this.DocumentView?.())) {
                 let added: boolean | undefined;
                 const hitDropTarget = (ele: HTMLElement, dropTarget: HTMLDivElement | null): boolean => {
                     if (!ele) return false;
diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
index 8784a709a..8bb7ddd9f 100644
--- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
@@ -43,7 +43,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._left > 0;
     }
 
-    constructor(props: Readonly<{}>) {
+    constructor(props: AntimodeMenuProps) {
         super(props);
 
         DirectionsAnchorMenu.Instance = this;
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 6f1f58a4c..fa863e123 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -105,9 +105,10 @@ export class CalendarBox extends CollectionSubView() {
 
     // TODO: Return a different color based on the event type
     eventToColor = (event: Doc): string => {
-        return 'red';
+        return 'red' + event;
     };
 
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     internalDocDrop = (e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) => {
         if (!super.onInternalDrop(e, de)) return false;
         de.complete.docDragData?.droppedDocuments.forEach(doc => {
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index 528bcd05a..6c3da8977 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -196,7 +196,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
             // Add CSV details to linked files
             this._linked_csv_files.push({
-                filename: CsvCast(newLinkedDoc.data).url.pathname,
+                filename: CsvCast(newLinkedDoc.data)?.url.pathname ?? '',
                 id: csvId,
                 text: csvData,
             });
@@ -634,6 +634,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     getDirectMatchingSegmentStart = (doc: Doc, citationText: string, indexesOfSegments: string[]): number => {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
         const originalSegments = JSON.parse(StrCast(doc.original_segments!)).map((segment: any, index: number) => ({
             index: index.toString(),
             text: segment.text,
@@ -877,7 +878,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return LinkManager.Instance.getAllRelatedLinks(this.Document)
             .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
             .map(d => DocCast(d?.annotationOn, d))
-            .filter(d => d);
+            .filter(d => d)
+            .map(d => d!);
     }
 
     /**
@@ -889,6 +891,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
             .map(d => DocCast(d?.annotationOn, d))
             .filter(d => d)
+            .map(d => d!)
             .filter(d => {
                 console.log(d.ai_doc_id);
                 return d.ai_doc_id;
@@ -905,15 +908,14 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             LinkManager.Instance.getAllRelatedLinks(this.Document)
                 .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
                 .map(d => DocCast(d?.annotationOn, d))
-                .filter(d => d)
-                .filter(d => d.summary)
+                .filter(d => d?.summary)
                 .map((doc, index) => {
-                    if (PDFCast(doc.data)) {
-                        return `<summary file_name="${PDFCast(doc.data).url.pathname}" applicable_tools=["rag"]>${doc.summary}</summary>`;
-                    } else if (CsvCast(doc.data)) {
-                        return `<summary file_name="${CsvCast(doc.data).url.pathname}" applicable_tools=["dataAnalysis"]>${doc.summary}</summary>`;
+                    if (PDFCast(doc?.data)) {
+                        return `<summary file_name="${PDFCast(doc!.data)!.url.pathname}" applicable_tools=["rag"]>${doc!.summary}</summary>`;
+                    } else if (CsvCast(doc?.data)) {
+                        return `<summary file_name="${CsvCast(doc!.data)!.url.pathname}" applicable_tools=["dataAnalysis"]>${doc!.summary}</summary>`;
                     } else {
-                        return `${index + 1}) ${doc.summary}`;
+                        return `${index + 1}) ${doc?.summary}`;
                     }
                 })
                 .join('\n') + '\n'
diff --git a/src/client/views/nodes/chatbot/tools/GetDocsTool.ts b/src/client/views/nodes/chatbot/tools/GetDocsTool.ts
index 05482a66e..42a7747d3 100644
--- a/src/client/views/nodes/chatbot/tools/GetDocsTool.ts
+++ b/src/client/views/nodes/chatbot/tools/GetDocsTool.ts
@@ -40,7 +40,10 @@ export class GetDocsTool extends BaseTool<GetDocsToolParamsType> {
     }
 
     async execute(args: ParametersType<GetDocsToolParamsType>): Promise<Observation[]> {
-        const docs = args.document_ids.map(doc_id => DocCast(DocServer.GetCachedRefField(doc_id)));
+        const docs = args.document_ids
+            .map(doc_id => DocCast(DocServer.GetCachedRefField(doc_id)))
+            .filter(d => d)
+            .map(d => d!);
         const collection = Docs.Create.FreeformDocument(docs, { title: args.title });
         this._docView._props.addDocTab(collection, OpenWhere.addRight);
         return [{ type: 'text', text: `Collection created in Dash called ${args.title}` }];
diff --git a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
index c1915a398..6d524e40f 100644
--- a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
+++ b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
@@ -15,7 +15,6 @@ import { Networking } from '../../../../Network';
 import { AI_Document, CHUNK_TYPE, RAGChunk } from '../types/types';
 import OpenAI from 'openai';
 import { Embedding } from 'openai/resources';
-import { PineconeEnvironmentVarsNotSupportedError } from '@pinecone-database/pinecone/dist/errors';
 
 dotenv.config();
 
@@ -101,7 +100,7 @@ export class Vectorstore {
         } else {
             // Start processing the document.
             doc.ai_document_status = 'PROGRESS';
-            const local_file_path: string = CsvCast(doc.data)?.url?.pathname ?? PDFCast(doc.data)?.url?.pathname ?? VideoCast(doc.data)?.url?.pathname ?? AudioCast(doc.data)?.url?.pathname;
+            const local_file_path = CsvCast(doc.data)?.url?.pathname ?? PDFCast(doc.data)?.url?.pathname ?? VideoCast(doc.data)?.url?.pathname ?? AudioCast(doc.data)?.url?.pathname;
 
             if (!local_file_path) {
                 console.log('Invalid file path.');
@@ -112,13 +111,13 @@ export class Vectorstore {
             let result: AI_Document & { doc_id: string };
             if (isAudioOrVideo) {
                 console.log('Processing media file...');
-                const response = await Networking.PostToServer('/processMediaFile', { fileName: path.basename(local_file_path) });
+                const response = (await Networking.PostToServer('/processMediaFile', { fileName: path.basename(local_file_path) })) as { [key: string]: unknown };
                 const segmentedTranscript = response.condensed;
                 console.log(segmentedTranscript);
-                const summary = response.summary;
+                const summary = response.summary as string;
                 doc.summary = summary;
                 // Generate embeddings for each chunk
-                const texts = segmentedTranscript.map((chunk: any) => chunk.text);
+                const texts = (segmentedTranscript as { text: string }[])?.map(chunk => chunk.text);
 
                 try {
                     const embeddingsResponse = await this.openai.embeddings.create({
@@ -138,7 +137,7 @@ export class Vectorstore {
                         file_name: local_file_path,
                         num_pages: 0,
                         summary: '',
-                        chunks: segmentedTranscript.map((chunk: any, index: number) => ({
+                        chunks: (segmentedTranscript as { text: string; start: number; end: number; indexes: string[] }[]).map((chunk, index) => ({
                             id: uuidv4(),
                             values: (embeddingsResponse.data as Embedding[])[index].embedding, // Assign embedding
                             metadata: {
@@ -173,7 +172,7 @@ export class Vectorstore {
             } else {
                 // Existing document processing logic remains unchanged
                 console.log('Processing regular document...');
-                const { jobId } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
+                const { jobId } = (await Networking.PostToServer('/createDocument', { file_path: local_file_path })) as { jobId: string };
 
                 while (true) {
                     await new Promise(resolve => setTimeout(resolve, 2000));
@@ -297,7 +296,7 @@ export class Vectorstore {
                 encoding_format: 'float',
             });
 
-            let queryEmbedding = queryEmbeddingResponse.data[0].embedding;
+            const queryEmbedding = queryEmbeddingResponse.data[0].embedding;
 
             // Extract the embedding from the response.
 
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 797a9b812..871c556e6 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -161,7 +161,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
                         // Check if doc or selection changed
                         if (!prevState.doc.eq(state.doc) || !prevState.selection.eq(state.selection)) {
-                            let found = false;
+                            const found = false;
                             const textToRemove = this.predictiveText;
 
                             state.doc.descendants((node, pos) => {
diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
index b241b27d7..6cfe9a62c 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
+++ b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
@@ -1,6 +1,6 @@
 import { action, makeObservable, observable } from 'mobx';
 import * as React from 'react';
-import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { emptyFunction } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 1891cfd4c..e8a5235c9 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -50,7 +50,7 @@ interface IAnnotationProps extends FieldViewProps {
 }
 @observer
 export class Annotation extends ObservableReactComponent<IAnnotationProps> {
-    constructor(props: any) {
+    constructor(props: IAnnotationProps) {
         super(props);
         makeObservable(this);
     }
@@ -58,7 +58,7 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> {
     @computed get linkHighlighted() {
         const found = LinkManager.Instance.getAllDirectLinks(this._props.annoDoc).find(link => {
             const a1 = Doc.getOppositeAnchor(link, this._props.annoDoc);
-            return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1));
+            return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1)!);
         });
         return found;
     }
@@ -112,6 +112,7 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> {
     outline = () => (this.linkHighlighted ? 'solid 1px lightBlue' : undefined);
     background = () => (this._props.annoDoc[Highlight] ? 'orange' : StrCast(this._props.annoDoc.backgroundColor));
     render() {
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         const forceRenderHack = [this.background(), this.outline(), this.opacity()]; // forces a re-render when these change -- because RegionAnnotation doesn't do this internally..
         return (
             <div style={{ display: this._props.annoDoc.textCopied && !Doc.GetBrushHighlightStatus(this._props.annoDoc) ? 'none' : undefined }}>
@@ -121,7 +122,7 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> {
                     .map(([x, y, width, height]) => (
                         <div
                             key={'' + x + y + width + height}
-                            style={{ pointerEvents: this._props.pointerEvents?.() as any }}
+                            style={{ pointerEvents: this._props.pointerEvents?.() as Property.PointerEvents }}
                             onPointerDown={this.onPointerDown}
                             onContextMenu={this.onContextMenu}
                             onPointerEnter={() => {
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index f60eb486e..cb837e3ab 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -162,7 +162,7 @@ export class FaceRecognitionHandler {
         });
         uniqueFaceDoc.$face = `Face${faceDocNum}`;
         uniqueFaceDoc.$face_annos = new List<Doc>();
-        Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
+        Doc.MyFaceCollection && Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
 
         Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
         return uniqueFaceDoc;
@@ -208,9 +208,9 @@ export class FaceRecognitionHandler {
             setTimeout(() => this.classifyFacesInImage(imgDoc), 1000);
         } else {
             const imgUrl = ImageCast(imgDoc[Doc.LayoutDataKey(imgDoc)]);
-            if (imgUrl && !DocListCast(Doc.MyFaceCollection.examinedFaceDocs).includes(imgDoc[DocData])) {
+            if (imgUrl && !DocListCast(Doc.MyFaceCollection?.examinedFaceDocs).includes(imgDoc[DocData])) {
                 // only examine Docs that have an image and that haven't already been examined.
-                Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc[DocData]);
+                Doc.MyFaceCollection && Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc[DocData]);
                 FaceRecognitionHandler.loadImage(imgUrl).then(
                     // load image and analyze faces
                     img => faceapi
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1c17dd369..ba94f0504 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -231,7 +231,7 @@ export class Doc extends RefField {
     public static AddLink: (link: Doc, checkExists?: boolean) => void;
     public static DeleteLink: (link: Doc) => void;
     public static Links: (link: Doc | undefined) => Doc[];
-    public static getOppositeAnchor: (linkDoc: Doc, anchor: Doc) => Doc | undefined;
+    public static getOppositeAnchor: (linkDoc: Doc | undefined, anchor: Doc | undefined) => Doc | undefined;
     // KeyValueBox SetField (defined there)
     public static SetField: (doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => boolean;
     // UserDoc "API"
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index e6755f828..ba2e9bb6f 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,5 +1,6 @@
 import { DateField } from './DateField';
 import { Doc, FieldType, FieldResult, Opt } from './Doc';
+import { InkField } from './InkField';
 import { List } from './List';
 import { ProxyField } from './Proxy';
 import { RefField } from './RefField';
@@ -99,6 +100,7 @@ export function VideoCast (field: FieldResult, defaultVal: VideoField | null = n
 export function AudioCast (field: FieldResult, defaultVal: AudioField | null = null)   { return Cast(field, AudioField, defaultVal); } // prettier-ignore
 export function PDFCast   (field: FieldResult, defaultVal: PdfField | null = null)     { return Cast(field, PdfField, defaultVal); } // prettier-ignore
 export function ImageCast (field: FieldResult, defaultVal: ImageField | null = null)   { return Cast(field, ImageField, defaultVal); } // prettier-ignore
+export function InkCast   (field: FieldResult, defaultVal: InkField | null = null)     { return Cast(field, InkField, defaultVal); } // prettier-ignore
 
 export function ImageCastToNameType(field: FieldResult, defaultVal: ImageField | null = null) {
     const href = ImageCast(field, defaultVal)?.url.href;
-- 
cgit v1.2.3-70-g09d2

