From 9d4bfd04760753b6fdd7ed81372ab8d85b615bc3 Mon Sep 17 00:00:00 2001 From: aaravkumar 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() @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() render() { return ( -
+
); -- cgit v1.2.3-70-g09d2 From 54bfa54b498f7a7c5813c2f5d0ff3005d6ff44ab Mon Sep 17 00:00:00 2001 From: bobzel 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() { 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 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() { - @observable journalDate: string; - public static LayoutString(fieldStr: string) { return FieldView.LayoutString(DailyJournal, fieldStr); } @@ -17,63 +14,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() 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 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 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 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() { + @observable journalDate: string; + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(DailyJournal, fieldStr); } @@ -14,8 +19,126 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() 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(); + render() { return (
() backgroundSize: '100% 20px', backgroundRepeat: 'repeat', }}> - + {/* GPT Button */} + + +
); } -- cgit v1.2.3-70-g09d2 From 5315ae28692e8214e6661d2336b6679fed9f90e2 Mon Sep 17 00:00:00 2001 From: aaravkumar 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() { @observable journalDate: string; + @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay + @observable lastUserText: string = ''; // Store last user-entered text + _ref = React.createRef(); // reference to the formatted textbox public static LayoutString(fieldStr: string) { return FieldView.LayoutString(DailyJournal, fieldStr); @@ -22,6 +25,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() 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() 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() 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() 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() } } + 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() 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(); - render() { return (
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() { @observable journalDate: string; @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay @observable lastUserText: string = ''; // Store last user-entered text _ref = React.createRef(); // 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() 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() 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() 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() 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 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() 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 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() * 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() 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 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() { @observable journalDate: string; @@ -109,7 +109,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() 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() 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() 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() 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() // 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() } @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 new FormattedTextBoxComment() }), + ...(plugs ?? []), ], }; } @@ -140,6 +141,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + 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 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 { 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(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(); 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) { )} - + {issueBody} 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 { 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 { @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 {