From 0dc1d055d1b278cf174dd40591a343b0e478db44 Mon Sep 17 00:00:00 2001 From: aaravkumar Date: Sun, 11 May 2025 14:00:41 -0400 Subject: cleaned up code, commented out console logs, added documentation, and added menu for pormpt generation --- src/client/views/nodes/TaskBox.tsx | 57 ++++++- .../views/nodes/formattedText/DailyJournal.tsx | 181 ++++++++++++++++++--- 2 files changed, 213 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx index dca01817f..ff1c70b90 100644 --- a/src/client/views/nodes/TaskBox.tsx +++ b/src/client/views/nodes/TaskBox.tsx @@ -9,26 +9,52 @@ import { Doc } from '../../../fields/Doc'; import './TaskBox.scss'; +/** + * Props (reference to document) for Task Box + */ + interface TaskBoxProps { Document: Doc; } +/** + * TaskBox class for adding task information + completing tasks + */ @observer export class TaskBox extends React.Component { + + /** + * Method to reuturn the + * @param fieldStr + * @returns + */ public static LayoutString(fieldStr: string) { return FieldView.LayoutString(TaskBox, fieldStr); } + /** + * Method to update the task description + * @param e - event of changing the description box input + */ + @action updateText = (e: React.ChangeEvent) => { this.props.Document.text = e.target.value; }; + /** + * Method to update the task title + * @param e - event of changing the title box input + */ @action updateTitle = (e: React.ChangeEvent) => { this.props.Document.title = e.target.value; }; + /** + * Method to update the all day status + * @param e - event of changing the all day checkbox + */ @action updateAllDay = (e: React.ChangeEvent) => { this.props.Document.$allDay = e.target.checked; @@ -41,6 +67,10 @@ export class TaskBox extends React.Component { this.setTaskDateRange(); }; + /** + * Method to update the task start time + * @param e - event of changing the start time input + */ @action updateStart = (e: React.ChangeEvent) => { const newStart = new Date(e.target.value); @@ -60,8 +90,10 @@ export class TaskBox extends React.Component { this.setTaskDateRange(); }; - - + /** + * Method to update the task end time + * @param e - event of changing the end time input + */ @action updateEnd = (e: React.ChangeEvent) => { const newEnd = new Date(e.target.value); @@ -81,9 +113,9 @@ export class TaskBox extends React.Component { this.setTaskDateRange(); }; - - - + /** + * Method to update the task date range + */ @action setTaskDateRange() { const doc = this.props.Document; @@ -107,11 +139,21 @@ export class TaskBox extends React.Component { } } + /** + * Method to set task's completion status + * @param e - event of changing the "completed" input checkbox + */ + @action toggleComplete = (e: React.ChangeEvent) => { this.props.Document.$completed = e.target.checked; }; + /** + * Constructor for the task box + * @param props - props containing the document reference + */ + constructor(props: TaskBoxProps) { super(props); makeObservable(this); @@ -150,6 +192,11 @@ export class TaskBox extends React.Component { this._widthDisposer?.(); } + /** + * Method to render the task box + * @returns - HTML with taskbox components + */ + render() { function toLocalDateTimeString(date: Date): string { diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx index d6d30dc13..269a609ef 100644 --- a/src/client/views/nodes/formattedText/DailyJournal.tsx +++ b/src/client/views/nodes/formattedText/DailyJournal.tsx @@ -10,11 +10,18 @@ import { RichTextField } from '../../../../fields/RichTextField'; import { Plugin } from 'prosemirror-state'; import { RTFCast } from '../../../../fields/Types'; import { Mark } from 'prosemirror-model'; +import { observer } from 'mobx-react'; + 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 + @observable typingTimeout: NodeJS.Timeout | null = null; // track typing delay + @observable lastUserText: string = ''; // store last user-entered text + @observable isLoadingPrompts: boolean = false; // track if prompts are loading + @observable showPromptMenu = false; + @observable inlinePromptsEnabled = true; + + _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?'; @@ -42,7 +49,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() month: 'long', day: 'numeric', }); - console.log('getFormattedDate():', date); + // console.log('getFormattedDate():', date); return date; } @@ -51,15 +58,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() */ @action setDailyTitle() { - console.log('setDailyTitle() called...'); - console.log('Current title before update:', this.dataDoc.title); + // 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); + // console.log('Updating title to:', this.journalDate); this.dataDoc.title = this.journalDate; } - console.log('New title after update:', this.dataDoc.title); + // console.log('New title after update:', this.dataDoc.title); } /** @@ -70,7 +77,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() const placeholderText = 'Start writing here...'; const dateText = `${this.journalDate}\n`; - console.log('Checking if dataDoc has text field...'); + // console.log('Checking if dataDoc has text field...'); this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat( [ @@ -82,9 +89,43 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() placeholderText.length ); - console.log('Current text field:', this.dataDoc[this.fieldKey]); + // console.log('Current text field:', this.dataDoc[this.fieldKey]); + } + + /** + * Method to show/hide the prompts menu + */ + @action.bound togglePromptMenu() { + this.showPromptMenu = !this.showPromptMenu; + } + + /** + * Method to toggle on/off inline predictive prompts + */ + @action.bound toggleInlinePrompts() { + this.inlinePromptsEnabled = !this.inlinePromptsEnabled; + } + + /** + * Method to handle click on document (to close prompt menu) + * @param e - a click on the document + */ + @action.bound + handleDocumentClick(e: MouseEvent) { + const menu = document.getElementById('prompts-menu'); + const button = document.getElementById('prompts-button'); + if ( + this.showPromptMenu && + menu && + !menu.contains(e.target as Node) && + button && + !button.contains(e.target as Node) + ) { + this.showPromptMenu = false; + } } + /** * Method to set initial date of document in the calendar view */ @@ -99,9 +140,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() 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); + // 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); + // console.log('Could not parse journalDate:', this.journalDate); } } } @@ -143,6 +184,8 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() */ @action insertPredictiveQuestion = async () => { + if (!this.inlinePromptsEnabled) return; + const editorView = this._ref.current?.EditorView; if (!editorView) return; @@ -196,6 +239,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() this.predictiveText = text; }; + /** + * Method to remove the predictive question upon type/click + * @returns - once predictive text is found, or all text has been checked + */ createPredictiveCleanupPlugin = () => { return new Plugin({ view: () => { @@ -213,7 +260,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() if (node.isText && node.text === textToRemove) { const tr = state.tr.delete(pos, pos + node.nodeSize); - // Set the desired default marks for future input + // default marks for input const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' }); const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' }); tr.setStoredMarks([]); @@ -244,8 +291,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() }; componentDidMount(): void { - console.log('componentDidMount() triggered...'); - console.log('Text: ' + RTFCast(this.Document.text)?.Text); + // console.log('componentDidMount() triggered...'); + document.addEventListener('mousedown', this.handleDocumentClick); + // console.log('Text: ' + RTFCast(this.Document.text)?.Text); const editorView = this._ref.current?.EditorView; if (editorView) { @@ -264,16 +312,17 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() const isDefaultTitle = isTitleString && currentTitle.includes('Untitled DailyJournal'); if (isTextEmpty && isDefaultTitle) { - console.log('Journal title and text are default. Initializing...'); + // console.log('Journal title and text are default. Initializing...'); this.setDailyTitle(); this.setDailyText(); this.setInitialDateRange(); } else { - console.log('Journal already has content. Skipping initialization.'); + // console.log('Journal already has content. Skipping initialization.'); } } componentWillUnmount(): void { + document.removeEventListener('mousedown', this.handleDocumentClick); const editorView = this._ref.current?.EditorView; if (editorView) { editorView.dom.removeEventListener('input', this.onTextInput); @@ -281,10 +330,20 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() if (this.typingTimeout) clearTimeout(this.typingTimeout); } + /** + * Method to generate pormpts via GPT + * @returns - if failed + */ @action handleGeneratePrompts = async () => { + if (this.isLoadingPrompts) { + return + } + + this.isLoadingPrompts = true; + 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); + // 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.'); @@ -321,12 +380,20 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() // Insert formatted text const transaction = state.tr.insert(state.selection.from, headerText).insert(state.selection.from + headerText.nodeSize, responseText); dispatch(transaction); + (this._props as any)?.updateLayout?.(); + } } catch (err) { console.error('Error calling GPT:', err); + } finally { + this.isLoadingPrompts = false; } }; + /** + * Method to render the styled DailyJournal + * @returns - the HTML component for the journal + */ render() { return (
() }}> {/* GPT Button */} + {this.showPromptMenu && ( +
+
+ + +
+ + +
+ )} + +
@@ -369,8 +508,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent() } } +const ObservedDailyJournal = observer(DailyJournal); + Docs.Prototypes.TemplateMap.set(DocumentType.JOURNAL, { - layout: { view: DailyJournal, dataField: 'text' }, + layout: { view: ObservedDailyJournal, dataField: 'text' }, options: { acl: '', _height: 35, -- cgit v1.2.3-70-g09d2