| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * LLM Chat Panel Widget | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | import BasicWidget from "../basic_widget.js"; | 
					
						
							|  |  |  | import toastService from "../../services/toast.js"; | 
					
						
							|  |  |  | import appContext from "../../components/app_context.js"; | 
					
						
							|  |  |  | import server from "../../services/server.js"; | 
					
						
							|  |  |  | import libraryLoader from "../../services/library_loader.js"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  | import { TPL, addMessageToChat, showSources, hideSources, showLoadingIndicator, hideLoadingIndicator } from "./ui.js"; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | import { formatMarkdown } from "./utils.js"; | 
					
						
							|  |  |  | import { createChatSession, checkSessionExists, setupStreamingResponse, getDirectResponse } from "./communication.js"; | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  | import { extractInChatToolSteps } from "./message_processor.js"; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | import { validateEmbeddingProviders } from "./validation.js"; | 
					
						
							|  |  |  | import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; | 
					
						
							|  |  |  | import { applySyntaxHighlight } from "../../services/syntax_highlight.js"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Import the LLM Chat CSS
 | 
					
						
							|  |  |  | (async function() { | 
					
						
							|  |  |  |     await libraryLoader.requireCss('stylesheets/llm_chat.css'); | 
					
						
							|  |  |  | })(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default class LlmChatPanel extends BasicWidget { | 
					
						
							|  |  |  |     private noteContextChatMessages!: HTMLElement; | 
					
						
							|  |  |  |     private noteContextChatForm!: HTMLFormElement; | 
					
						
							|  |  |  |     private noteContextChatInput!: HTMLTextAreaElement; | 
					
						
							|  |  |  |     private noteContextChatSendButton!: HTMLButtonElement; | 
					
						
							|  |  |  |     private chatContainer!: HTMLElement; | 
					
						
							|  |  |  |     private loadingIndicator!: HTMLElement; | 
					
						
							|  |  |  |     private sourcesList!: HTMLElement; | 
					
						
							|  |  |  |     private sourcesContainer!: HTMLElement; | 
					
						
							|  |  |  |     private sourcesCount!: HTMLElement; | 
					
						
							|  |  |  |     private useAdvancedContextCheckbox!: HTMLInputElement; | 
					
						
							|  |  |  |     private showThinkingCheckbox!: HTMLInputElement; | 
					
						
							|  |  |  |     private validationWarning!: HTMLElement; | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |     private chatNoteId: string | null = null; | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |     private noteId: string | null = null; // The actual noteId for the Chat Note
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     private currentNoteId: string | null = null; | 
					
						
							|  |  |  |     private _messageHandlerId: number | null = null; | 
					
						
							|  |  |  |     private _messageHandler: any = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Callbacks for data persistence
 | 
					
						
							|  |  |  |     private onSaveData: ((data: any) => Promise<void>) | null = null; | 
					
						
							|  |  |  |     private onGetData: (() => Promise<any>) | null = null; | 
					
						
							|  |  |  |     private messages: MessageData[] = []; | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |     private sources: Array<{noteId: string; title: string; similarity?: number; content?: string}> = []; | 
					
						
							|  |  |  |     private metadata: { | 
					
						
							|  |  |  |         model?: string; | 
					
						
							|  |  |  |         provider?: string; | 
					
						
							|  |  |  |         temperature?: number; | 
					
						
							|  |  |  |         maxTokens?: number; | 
					
						
							|  |  |  |         toolExecutions?: Array<{ | 
					
						
							|  |  |  |             id: string; | 
					
						
							|  |  |  |             name: string; | 
					
						
							|  |  |  |             arguments: any; | 
					
						
							|  |  |  |             result: any; | 
					
						
							|  |  |  |             error?: string; | 
					
						
							|  |  |  |             timestamp: string; | 
					
						
							|  |  |  |         }>; | 
					
						
							|  |  |  |         lastUpdated?: string; | 
					
						
							|  |  |  |         usage?: { | 
					
						
							|  |  |  |             promptTokens?: number; | 
					
						
							|  |  |  |             completionTokens?: number; | 
					
						
							|  |  |  |             totalTokens?: number; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } = { | 
					
						
							|  |  |  |         model: 'default', | 
					
						
							|  |  |  |         temperature: 0.7, | 
					
						
							|  |  |  |         toolExecutions: [] | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Public getters and setters for private properties
 | 
					
						
							|  |  |  |     public getCurrentNoteId(): string | null { | 
					
						
							|  |  |  |         return this.currentNoteId; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public setCurrentNoteId(noteId: string | null): void { | 
					
						
							|  |  |  |         this.currentNoteId = noteId; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public getMessages(): MessageData[] { | 
					
						
							|  |  |  |         return this.messages; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public setMessages(messages: MessageData[]): void { | 
					
						
							|  |  |  |         this.messages = messages; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |     public getChatNoteId(): string | null { | 
					
						
							|  |  |  |         return this.chatNoteId; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |     public setChatNoteId(chatNoteId: string | null): void { | 
					
						
							|  |  |  |         this.chatNoteId = chatNoteId; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public getNoteContextChatMessages(): HTMLElement { | 
					
						
							|  |  |  |         return this.noteContextChatMessages; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public clearNoteContextChatMessages(): void { | 
					
						
							|  |  |  |         this.noteContextChatMessages.innerHTML = ''; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     doRender() { | 
					
						
							|  |  |  |         this.$widget = $(TPL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const element = this.$widget[0]; | 
					
						
							|  |  |  |         this.noteContextChatMessages = element.querySelector('.note-context-chat-messages') as HTMLElement; | 
					
						
							|  |  |  |         this.noteContextChatForm = element.querySelector('.note-context-chat-form') as HTMLFormElement; | 
					
						
							|  |  |  |         this.noteContextChatInput = element.querySelector('.note-context-chat-input') as HTMLTextAreaElement; | 
					
						
							|  |  |  |         this.noteContextChatSendButton = element.querySelector('.note-context-chat-send-button') as HTMLButtonElement; | 
					
						
							|  |  |  |         this.chatContainer = element.querySelector('.note-context-chat-container') as HTMLElement; | 
					
						
							|  |  |  |         this.loadingIndicator = element.querySelector('.loading-indicator') as HTMLElement; | 
					
						
							|  |  |  |         this.sourcesList = element.querySelector('.sources-list') as HTMLElement; | 
					
						
							|  |  |  |         this.sourcesContainer = element.querySelector('.sources-container') as HTMLElement; | 
					
						
							|  |  |  |         this.sourcesCount = element.querySelector('.sources-count') as HTMLElement; | 
					
						
							|  |  |  |         this.useAdvancedContextCheckbox = element.querySelector('.use-advanced-context-checkbox') as HTMLInputElement; | 
					
						
							|  |  |  |         this.showThinkingCheckbox = element.querySelector('.show-thinking-checkbox') as HTMLInputElement; | 
					
						
							|  |  |  |         this.validationWarning = element.querySelector('.provider-validation-warning') as HTMLElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Set up event delegation for the settings link
 | 
					
						
							|  |  |  |         this.validationWarning.addEventListener('click', (e) => { | 
					
						
							|  |  |  |             const target = e.target as HTMLElement; | 
					
						
							|  |  |  |             if (target.classList.contains('settings-link') || target.closest('.settings-link')) { | 
					
						
							|  |  |  |                 console.log('Settings link clicked, navigating to AI settings URL'); | 
					
						
							|  |  |  |                 window.location.href = '#root/_hidden/_options/_optionsAi'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.initializeEventListeners(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return this.$widget; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cleanup() { | 
					
						
							|  |  |  |         console.log(`LlmChatPanel cleanup called, removing any active WebSocket subscriptions`); | 
					
						
							|  |  |  |         this._messageHandler = null; | 
					
						
							|  |  |  |         this._messageHandlerId = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Set the callbacks for data persistence | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     setDataCallbacks( | 
					
						
							|  |  |  |         saveDataCallback: (data: any) => Promise<void>, | 
					
						
							|  |  |  |         getDataCallback: () => Promise<any> | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         this.onSaveData = saveDataCallback; | 
					
						
							|  |  |  |         this.onGetData = getDataCallback; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Save current chat data to the note attribute | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     async saveCurrentData() { | 
					
						
							|  |  |  |         if (!this.onSaveData) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             // Extract current tool execution steps if any exist
 | 
					
						
							|  |  |  |             const toolSteps = extractInChatToolSteps(this.noteContextChatMessages); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             // Get tool executions from both UI and any cached executions in metadata
 | 
					
						
							|  |  |  |             let toolExecutions: Array<{ | 
					
						
							|  |  |  |                 id: string; | 
					
						
							|  |  |  |                 name: string; | 
					
						
							|  |  |  |                 arguments: any; | 
					
						
							|  |  |  |                 result: any; | 
					
						
							|  |  |  |                 error?: string; | 
					
						
							|  |  |  |                 timestamp: string; | 
					
						
							|  |  |  |             }> = []; | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             // First include any tool executions already in metadata (from streaming events)
 | 
					
						
							|  |  |  |             if (this.metadata?.toolExecutions && Array.isArray(this.metadata.toolExecutions)) { | 
					
						
							|  |  |  |                 toolExecutions = [...this.metadata.toolExecutions]; | 
					
						
							|  |  |  |                 console.log(`Including ${toolExecutions.length} tool executions from metadata`); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             // Also extract any visible tool steps from the UI
 | 
					
						
							|  |  |  |             const extractedExecutions = toolSteps.map(step => { | 
					
						
							|  |  |  |                 // Parse tool execution information
 | 
					
						
							|  |  |  |                 if (step.type === 'tool-execution') { | 
					
						
							|  |  |  |                     try { | 
					
						
							|  |  |  |                         const content = JSON.parse(step.content); | 
					
						
							|  |  |  |                         return { | 
					
						
							|  |  |  |                             id: content.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, | 
					
						
							|  |  |  |                             name: content.tool || 'unknown', | 
					
						
							|  |  |  |                             arguments: content.args || {}, | 
					
						
							|  |  |  |                             result: content.result || {}, | 
					
						
							|  |  |  |                             error: content.error, | 
					
						
							|  |  |  |                             timestamp: new Date().toISOString() | 
					
						
							|  |  |  |                         }; | 
					
						
							|  |  |  |                     } catch (e) { | 
					
						
							|  |  |  |                         // If we can't parse it, create a basic record
 | 
					
						
							|  |  |  |                         return { | 
					
						
							|  |  |  |                             id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, | 
					
						
							|  |  |  |                             name: 'unknown', | 
					
						
							|  |  |  |                             arguments: {}, | 
					
						
							|  |  |  |                             result: step.content, | 
					
						
							|  |  |  |                             timestamp: new Date().toISOString() | 
					
						
							|  |  |  |                         }; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } else if (step.type === 'result' && step.name) { | 
					
						
							|  |  |  |                     // Handle result steps with a name
 | 
					
						
							|  |  |  |                     return { | 
					
						
							|  |  |  |                         id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, | 
					
						
							|  |  |  |                         name: step.name, | 
					
						
							|  |  |  |                         arguments: {}, | 
					
						
							|  |  |  |                         result: step.content, | 
					
						
							|  |  |  |                         timestamp: new Date().toISOString() | 
					
						
							|  |  |  |                     }; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return { | 
					
						
							|  |  |  |                     id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, | 
					
						
							|  |  |  |                     name: 'unknown', | 
					
						
							|  |  |  |                     arguments: {}, | 
					
						
							|  |  |  |                     result: 'Unrecognized tool step', | 
					
						
							|  |  |  |                     timestamp: new Date().toISOString() | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             // Merge the tool executions, keeping only unique IDs
 | 
					
						
							|  |  |  |             const existingIds = new Set(toolExecutions.map((t: {id: string}) => t.id)); | 
					
						
							|  |  |  |             for (const exec of extractedExecutions) { | 
					
						
							|  |  |  |                 if (!existingIds.has(exec.id)) { | 
					
						
							|  |  |  |                     toolExecutions.push(exec); | 
					
						
							|  |  |  |                     existingIds.add(exec.id); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             const dataToSave: ChatData = { | 
					
						
							|  |  |  |                 messages: this.messages, | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                 chatNoteId: this.chatNoteId, | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                 noteId: this.noteId, | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 toolSteps: toolSteps, | 
					
						
							|  |  |  |                 // Add sources if we have them
 | 
					
						
							|  |  |  |                 sources: this.sources || [], | 
					
						
							|  |  |  |                 // Add metadata
 | 
					
						
							|  |  |  |                 metadata: { | 
					
						
							|  |  |  |                     model: this.metadata?.model || 'default', | 
					
						
							|  |  |  |                     provider: this.metadata?.provider || undefined, | 
					
						
							|  |  |  |                     temperature: this.metadata?.temperature || 0.7, | 
					
						
							|  |  |  |                     lastUpdated: new Date().toISOString(), | 
					
						
							|  |  |  |                     // Add tool executions
 | 
					
						
							|  |  |  |                     toolExecutions: toolExecutions | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             console.log(`Saving chat data with chatNoteId: ${this.chatNoteId}, noteId: ${this.noteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Save the data to the note attribute via the callback
 | 
					
						
							| 
									
										
										
										
											2025-04-16 20:09:26 +00:00
										 |  |  |             // This is the ONLY place we should save data, letting the container widget handle persistence
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             await this.onSaveData(dataToSave); | 
					
						
							|  |  |  |         } catch (error) { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             console.error('Error saving chat data:', error); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Load saved chat data from the note attribute | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     async loadSavedData(): Promise<boolean> { | 
					
						
							|  |  |  |         if (!this.onGetData) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             const savedData = await this.onGetData() as ChatData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (savedData?.messages?.length > 0) { | 
					
						
							|  |  |  |                 // Load messages
 | 
					
						
							|  |  |  |                 this.messages = savedData.messages; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Clear and rebuild the chat UI
 | 
					
						
							|  |  |  |                 this.noteContextChatMessages.innerHTML = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.messages.forEach(message => { | 
					
						
							|  |  |  |                     const role = message.role as 'user' | 'assistant'; | 
					
						
							|  |  |  |                     this.addMessageToChat(role, message.content); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Restore tool execution steps if they exist
 | 
					
						
							|  |  |  |                 if (savedData.toolSteps && Array.isArray(savedData.toolSteps) && savedData.toolSteps.length > 0) { | 
					
						
							|  |  |  |                     console.log(`Restoring ${savedData.toolSteps.length} saved tool steps`); | 
					
						
							|  |  |  |                     this.restoreInChatToolSteps(savedData.toolSteps); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Load sources if available
 | 
					
						
							|  |  |  |                 if (savedData.sources && Array.isArray(savedData.sources)) { | 
					
						
							|  |  |  |                     this.sources = savedData.sources; | 
					
						
							|  |  |  |                     console.log(`Loaded ${this.sources.length} sources from saved data`); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Show sources in the UI if they exist
 | 
					
						
							|  |  |  |                     if (this.sources.length > 0) { | 
					
						
							|  |  |  |                         this.showSources(this.sources); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Load metadata if available
 | 
					
						
							|  |  |  |                 if (savedData.metadata) { | 
					
						
							|  |  |  |                     this.metadata = { | 
					
						
							|  |  |  |                         ...this.metadata, | 
					
						
							|  |  |  |                         ...savedData.metadata | 
					
						
							|  |  |  |                     }; | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Ensure tool executions are loaded
 | 
					
						
							|  |  |  |                     if (savedData.metadata.toolExecutions && Array.isArray(savedData.metadata.toolExecutions)) { | 
					
						
							|  |  |  |                         console.log(`Loaded ${savedData.metadata.toolExecutions.length} tool executions from saved data`); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                         if (!this.metadata.toolExecutions) { | 
					
						
							|  |  |  |                             this.metadata.toolExecutions = []; | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                         // Make sure we don't lose any tool executions
 | 
					
						
							|  |  |  |                         this.metadata.toolExecutions = savedData.metadata.toolExecutions; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     console.log(`Loaded metadata from saved data:`, this.metadata); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                 // Load Chat Note ID if available
 | 
					
						
							|  |  |  |                 if (savedData.chatNoteId) { | 
					
						
							|  |  |  |                     console.log(`Setting Chat Note ID from saved data: ${savedData.chatNoteId}`); | 
					
						
							|  |  |  |                     this.chatNoteId = savedData.chatNoteId; | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                     // Set the noteId as well - this could be different from the chatNoteId
 | 
					
						
							|  |  |  |                     // If we have a separate noteId stored, use it, otherwise default to the chatNoteId
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                     if (savedData.noteId) { | 
					
						
							|  |  |  |                         this.noteId = savedData.noteId; | 
					
						
							|  |  |  |                         console.log(`Using stored Chat Note ID: ${this.noteId}`); | 
					
						
							|  |  |  |                     } else { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                         // For compatibility with older data, use the chatNoteId as the noteId
 | 
					
						
							|  |  |  |                         this.noteId = savedData.chatNoteId; | 
					
						
							|  |  |  |                         console.log(`No Chat Note ID found, using Chat Note ID: ${this.chatNoteId}`); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     // No need to check if session exists on server since the Chat Note
 | 
					
						
							|  |  |  |                     // is now the source of truth - if we have the Note, we have the session
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                 } else { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                     // For backward compatibility, try to get sessionId
 | 
					
						
							|  |  |  |                     if ((savedData as any).sessionId) { | 
					
						
							|  |  |  |                         console.log(`Using legacy sessionId as Chat Note ID: ${(savedData as any).sessionId}`); | 
					
						
							|  |  |  |                         this.chatNoteId = (savedData as any).sessionId; | 
					
						
							|  |  |  |                         this.noteId = savedData.noteId || (savedData as any).sessionId; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         // No saved Chat Note ID, create a new one
 | 
					
						
							|  |  |  |                         this.chatNoteId = null; | 
					
						
							|  |  |  |                         this.noteId = null; | 
					
						
							|  |  |  |                         await this.createChatSession(); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } catch (error) { | 
					
						
							|  |  |  |             console.error('Failed to load saved chat data', error); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Restore tool execution steps in the chat UI | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private restoreInChatToolSteps(steps: ToolExecutionStep[]) { | 
					
						
							|  |  |  |         if (!steps || steps.length === 0) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Create the tool execution element
 | 
					
						
							|  |  |  |         const toolExecutionElement = document.createElement('div'); | 
					
						
							|  |  |  |         toolExecutionElement.className = 'chat-tool-execution mb-3'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Insert before the assistant message if it exists
 | 
					
						
							|  |  |  |         const assistantMessage = this.noteContextChatMessages.querySelector('.assistant-message:last-child'); | 
					
						
							|  |  |  |         if (assistantMessage) { | 
					
						
							|  |  |  |             this.noteContextChatMessages.insertBefore(toolExecutionElement, assistantMessage); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // Otherwise append to the end
 | 
					
						
							|  |  |  |             this.noteContextChatMessages.appendChild(toolExecutionElement); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Fill with tool execution content
 | 
					
						
							|  |  |  |         toolExecutionElement.innerHTML = `
 | 
					
						
							|  |  |  |             <div class="tool-execution-container p-2 rounded mb-2"> | 
					
						
							|  |  |  |                 <div class="tool-execution-header d-flex align-items-center justify-content-between mb-2"> | 
					
						
							|  |  |  |                     <div> | 
					
						
							|  |  |  |                         <i class="bx bx-code-block text-primary me-2"></i> | 
					
						
							|  |  |  |                         <span class="fw-bold">Tool Execution</span> | 
					
						
							|  |  |  |                     </div> | 
					
						
							|  |  |  |                     <button type="button" class="btn btn-sm btn-link p-0 text-muted tool-execution-chat-clear" title="Clear tool execution history"> | 
					
						
							|  |  |  |                         <i class="bx bx-x"></i> | 
					
						
							|  |  |  |                     </button> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |                 <div class="tool-execution-chat-steps"> | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |                     ${this.renderToolStepsHtml(steps)} | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                 </div> | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |         `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add event listener for the clear button
 | 
					
						
							|  |  |  |         const clearButton = toolExecutionElement.querySelector('.tool-execution-chat-clear'); | 
					
						
							|  |  |  |         if (clearButton) { | 
					
						
							|  |  |  |             clearButton.addEventListener('click', (e) => { | 
					
						
							|  |  |  |                 e.preventDefault(); | 
					
						
							|  |  |  |                 e.stopPropagation(); | 
					
						
							|  |  |  |                 toolExecutionElement.remove(); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Render HTML for tool execution steps | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private renderToolStepsHtml(steps: ToolExecutionStep[]): string { | 
					
						
							|  |  |  |         if (!steps || steps.length === 0) return ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return steps.map(step => { | 
					
						
							|  |  |  |             let icon = 'bx-info-circle'; | 
					
						
							|  |  |  |             let className = 'info'; | 
					
						
							|  |  |  |             let content = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (step.type === 'executing') { | 
					
						
							|  |  |  |                 icon = 'bx-code-block'; | 
					
						
							|  |  |  |                 className = 'executing'; | 
					
						
							|  |  |  |                 content = `<div>${step.content || 'Executing tools...'}</div>`; | 
					
						
							|  |  |  |             } else if (step.type === 'result') { | 
					
						
							|  |  |  |                 icon = 'bx-terminal'; | 
					
						
							|  |  |  |                 className = 'result'; | 
					
						
							|  |  |  |                 content = `
 | 
					
						
							|  |  |  |                     <div>Tool: <strong>${step.name || 'unknown'}</strong></div> | 
					
						
							|  |  |  |                     <div class="mt-1 ps-3">${step.content || ''}</div> | 
					
						
							|  |  |  |                 `;
 | 
					
						
							|  |  |  |             } else if (step.type === 'error') { | 
					
						
							|  |  |  |                 icon = 'bx-error-circle'; | 
					
						
							|  |  |  |                 className = 'error'; | 
					
						
							|  |  |  |                 content = `
 | 
					
						
							|  |  |  |                     <div>Tool: <strong>${step.name || 'unknown'}</strong></div> | 
					
						
							|  |  |  |                     <div class="mt-1 ps-3 text-danger">${step.content || 'Error occurred'}</div> | 
					
						
							|  |  |  |                 `;
 | 
					
						
							|  |  |  |             } else if (step.type === 'generating') { | 
					
						
							|  |  |  |                 icon = 'bx-message-dots'; | 
					
						
							|  |  |  |                 className = 'generating'; | 
					
						
							|  |  |  |                 content = `<div>${step.content || 'Generating response...'}</div>`; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return `
 | 
					
						
							|  |  |  |                 <div class="tool-step ${className} p-2 mb-2 rounded"> | 
					
						
							|  |  |  |                     <div class="d-flex align-items-center"> | 
					
						
							|  |  |  |                         <i class="bx ${icon} me-2"></i> | 
					
						
							|  |  |  |                         ${content} | 
					
						
							|  |  |  |                     </div> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             `;
 | 
					
						
							|  |  |  |         }).join(''); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     async refresh() { | 
					
						
							|  |  |  |         if (!this.isVisible()) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Check for any provider validation issues when refreshing
 | 
					
						
							|  |  |  |         await validateEmbeddingProviders(this.validationWarning); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get current note context if needed
 | 
					
						
							|  |  |  |         const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // If we're switching to a different note, we need to reset
 | 
					
						
							|  |  |  |         if (this.currentNoteId !== currentActiveNoteId) { | 
					
						
							|  |  |  |             console.log(`Note ID changed from ${this.currentNoteId} to ${currentActiveNoteId}, resetting chat panel`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Reset the UI and data
 | 
					
						
							|  |  |  |             this.noteContextChatMessages.innerHTML = ''; | 
					
						
							|  |  |  |             this.messages = []; | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             this.chatNoteId = null; | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             this.noteId = null; // Also reset the chat note ID
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             this.hideSources(); // Hide any sources from previous note
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Update our current noteId
 | 
					
						
							|  |  |  |             this.currentNoteId = currentActiveNoteId; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Always try to load saved data for the current note
 | 
					
						
							|  |  |  |         const hasSavedData = await this.loadSavedData(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Only create a new session if we don't have a session or saved data
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |         if (!this.chatNoteId || !this.noteId || !hasSavedData) { | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             // Create a new chat session
 | 
					
						
							|  |  |  |             await this.createChatSession(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private async createChatSession() { | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Create a new Chat Note to represent this chat session
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             // The function now returns both chatNoteId and noteId
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             const result = await createChatSession(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             if (!result.chatNoteId) { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                 toastService.showError('Failed to create chat session'); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             console.log(`Created new chat session with ID: ${result.chatNoteId}`); | 
					
						
							|  |  |  |             this.chatNoteId = result.chatNoteId; | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // If the API returned a noteId directly, use it
 | 
					
						
							|  |  |  |             if (result.noteId) { | 
					
						
							|  |  |  |                 this.noteId = result.noteId; | 
					
						
							|  |  |  |                 console.log(`Using noteId from API response: ${this.noteId}`); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // Otherwise, try to get session details to find the noteId
 | 
					
						
							|  |  |  |                 try { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                     const sessionDetails = await server.get<any>(`llm/chat/${this.chatNoteId}`); | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                     if (sessionDetails && sessionDetails.noteId) { | 
					
						
							|  |  |  |                         this.noteId = sessionDetails.noteId; | 
					
						
							|  |  |  |                         console.log(`Using noteId from session details: ${this.noteId}`); | 
					
						
							|  |  |  |                     } else { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                         // As a last resort, use the current note ID
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                         console.warn(`No noteId found in session details, using parent note ID: ${this.currentNoteId}`); | 
					
						
							|  |  |  |                         this.noteId = this.currentNoteId; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } catch (detailsError) { | 
					
						
							|  |  |  |                     console.error('Could not fetch session details:', detailsError); | 
					
						
							|  |  |  |                     // Use current note ID as a fallback
 | 
					
						
							|  |  |  |                     this.noteId = this.currentNoteId; | 
					
						
							|  |  |  |                     console.warn(`Using current note ID as fallback: ${this.noteId}`); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Verify that the noteId is valid
 | 
					
						
							|  |  |  |             if (this.noteId !== this.currentNoteId) { | 
					
						
							|  |  |  |                 console.log(`Note ID verification - session's noteId: ${this.noteId}, current note: ${this.currentNoteId}`); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Save the session ID and data
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } catch (error) { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             console.error('Error creating chat session:', error); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             toastService.showError('Failed to create chat session'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Handle sending a user message to the LLM service | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private async sendMessage(content: string) { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         if (!content.trim()) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add the user message to the UI and data model
 | 
					
						
							|  |  |  |         this.addMessageToChat('user', content); | 
					
						
							|  |  |  |         this.messages.push({ | 
					
						
							|  |  |  |             role: 'user', | 
					
						
							|  |  |  |             content: content | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Save the data immediately after a user message
 | 
					
						
							|  |  |  |         await this.saveCurrentData(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Clear input and show loading state
 | 
					
						
							|  |  |  |         this.noteContextChatInput.value = ''; | 
					
						
							|  |  |  |         showLoadingIndicator(this.loadingIndicator); | 
					
						
							|  |  |  |         this.hideSources(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             const useAdvancedContext = this.useAdvancedContextCheckbox.checked; | 
					
						
							|  |  |  |             const showThinking = this.showThinkingCheckbox.checked; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add logging to verify parameters
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.chatNoteId}`); | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Create the message parameters
 | 
					
						
							|  |  |  |             const messageParams = { | 
					
						
							|  |  |  |                 content, | 
					
						
							|  |  |  |                 useAdvancedContext, | 
					
						
							|  |  |  |                 showThinking | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Try websocket streaming (preferred method)
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 await this.setupStreamingResponse(messageParams); | 
					
						
							|  |  |  |             } catch (streamingError) { | 
					
						
							|  |  |  |                 console.warn("WebSocket streaming failed, falling back to direct response:", streamingError); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // If streaming fails, fall back to direct response
 | 
					
						
							|  |  |  |                 const handled = await this.handleDirectResponse(messageParams); | 
					
						
							|  |  |  |                 if (!handled) { | 
					
						
							|  |  |  |                     // If neither method works, show an error
 | 
					
						
							|  |  |  |                     throw new Error("Failed to get response from server"); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Save the final state to the Chat Note after getting the response
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							|  |  |  |         } catch (error) { | 
					
						
							|  |  |  |             console.error('Error processing user message:', error); | 
					
						
							|  |  |  |             toastService.showError('Failed to process message'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add a generic error message to the UI
 | 
					
						
							|  |  |  |             this.addMessageToChat('assistant', 'Sorry, I encountered an error processing your message. Please try again.'); | 
					
						
							|  |  |  |             this.messages.push({ | 
					
						
							|  |  |  |                 role: 'assistant', | 
					
						
							|  |  |  |                 content: 'Sorry, I encountered an error processing your message. Please try again.' | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Save the data even after error
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Process a new user message - add to UI and save | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private async processUserMessage(content: string) { | 
					
						
							|  |  |  |         // Check for validation issues first
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         await validateEmbeddingProviders(this.validationWarning); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Make sure we have a valid session
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |         if (!this.chatNoteId) { | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             // If no session ID, create a new session
 | 
					
						
							|  |  |  |             await this.createChatSession(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             if (!this.chatNoteId) { | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                 // If still no session ID, show error and return
 | 
					
						
							|  |  |  |                 console.error("Failed to create chat session"); | 
					
						
							|  |  |  |                 toastService.showError("Failed to create chat session"); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         // Add user message to messages array if not already added
 | 
					
						
							|  |  |  |         if (!this.messages.some(msg => msg.role === 'user' && msg.content === content)) { | 
					
						
							|  |  |  |             this.messages.push({ | 
					
						
							|  |  |  |                 role: 'user', | 
					
						
							|  |  |  |                 content: content | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Clear input and show loading state
 | 
					
						
							|  |  |  |         this.noteContextChatInput.value = ''; | 
					
						
							|  |  |  |         showLoadingIndicator(this.loadingIndicator); | 
					
						
							|  |  |  |         this.hideSources(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             const useAdvancedContext = this.useAdvancedContextCheckbox.checked; | 
					
						
							|  |  |  |             const showThinking = this.showThinkingCheckbox.checked; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Save current state to the Chat Note before getting a response
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             // Add logging to verify parameters
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.chatNoteId}`); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Create the message parameters
 | 
					
						
							|  |  |  |             const messageParams = { | 
					
						
							|  |  |  |                 content, | 
					
						
							|  |  |  |                 useAdvancedContext, | 
					
						
							|  |  |  |                 showThinking | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Try websocket streaming (preferred method)
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 await this.setupStreamingResponse(messageParams); | 
					
						
							|  |  |  |             } catch (streamingError) { | 
					
						
							|  |  |  |                 console.warn("WebSocket streaming failed, falling back to direct response:", streamingError); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // If streaming fails, fall back to direct response
 | 
					
						
							|  |  |  |                 const handled = await this.handleDirectResponse(messageParams); | 
					
						
							|  |  |  |                 if (!handled) { | 
					
						
							|  |  |  |                     // If neither method works, show an error
 | 
					
						
							|  |  |  |                     throw new Error("Failed to get response from server"); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Save final state after getting the response
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } catch (error) { | 
					
						
							|  |  |  |             this.handleError(error as Error); | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Make sure we save the current state even on error
 | 
					
						
							|  |  |  |             await this.saveCurrentData(); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Try to get a direct response from the server | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private async handleDirectResponse(messageParams: any): Promise<boolean> { | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             if (!this.chatNoteId) return false; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             console.log(`Getting direct response using sessionId: ${this.chatNoteId} (noteId: ${this.noteId})`); | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             // Get a direct response from the server
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             const postResponse = await getDirectResponse(this.chatNoteId, messageParams); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // If the POST request returned content directly, display it
 | 
					
						
							|  |  |  |             if (postResponse && postResponse.content) { | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Store metadata from the response
 | 
					
						
							|  |  |  |                 if (postResponse.metadata) { | 
					
						
							|  |  |  |                     console.log("Received metadata from response:", postResponse.metadata); | 
					
						
							|  |  |  |                     this.metadata = { | 
					
						
							|  |  |  |                         ...this.metadata, | 
					
						
							|  |  |  |                         ...postResponse.metadata | 
					
						
							|  |  |  |                     }; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Store sources from the response
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                 if (postResponse.sources && postResponse.sources.length > 0) { | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     console.log(`Received ${postResponse.sources.length} sources from response`); | 
					
						
							|  |  |  |                     this.sources = postResponse.sources; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |                     this.showSources(postResponse.sources); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Process the assistant response
 | 
					
						
							|  |  |  |                 this.processAssistantResponse(postResponse.content, postResponse); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 hideLoadingIndicator(this.loadingIndicator); | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } catch (error) { | 
					
						
							|  |  |  |             console.error("Error with direct response:", error); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Process an assistant response - add to UI and save | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |     private async processAssistantResponse(content: string, fullResponse?: any) { | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         // Add the response to the chat UI
 | 
					
						
							|  |  |  |         this.addMessageToChat('assistant', content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add to our local message array too
 | 
					
						
							|  |  |  |         this.messages.push({ | 
					
						
							|  |  |  |             role: 'assistant', | 
					
						
							|  |  |  |             content, | 
					
						
							|  |  |  |             timestamp: new Date() | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |         // If we received tool execution information, add it to metadata
 | 
					
						
							|  |  |  |         if (fullResponse?.metadata?.toolExecutions) { | 
					
						
							|  |  |  |             console.log(`Storing ${fullResponse.metadata.toolExecutions.length} tool executions from response`); | 
					
						
							|  |  |  |             // Make sure our metadata has toolExecutions
 | 
					
						
							|  |  |  |             if (!this.metadata.toolExecutions) { | 
					
						
							|  |  |  |                 this.metadata.toolExecutions = []; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             // Add new tool executions
 | 
					
						
							|  |  |  |             this.metadata.toolExecutions = [ | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  |                 ...this.metadata.toolExecutions, | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 ...fullResponse.metadata.toolExecutions | 
					
						
							|  |  |  |             ]; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Save to note
 | 
					
						
							|  |  |  |         this.saveCurrentData().catch(err => { | 
					
						
							|  |  |  |             console.error("Failed to save assistant response to note:", err); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Set up streaming response via WebSocket | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private async setupStreamingResponse(messageParams: any): Promise<void> { | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |         if (!this.chatNoteId) { | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             throw new Error("No session ID available"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |         console.log(`Setting up streaming response using sessionId: ${this.chatNoteId} (noteId: ${this.noteId})`); | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |         // Store tool executions captured during streaming
 | 
					
						
							|  |  |  |         const toolExecutionsCache: Array<{ | 
					
						
							|  |  |  |             id: string; | 
					
						
							|  |  |  |             name: string; | 
					
						
							|  |  |  |             arguments: any; | 
					
						
							|  |  |  |             result: any; | 
					
						
							|  |  |  |             error?: string; | 
					
						
							|  |  |  |             timestamp: string; | 
					
						
							|  |  |  |         }> = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         return setupStreamingResponse( | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |             this.chatNoteId, | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             messageParams, | 
					
						
							|  |  |  |             // Content update handler
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |             (content: string, isDone: boolean = false) => { | 
					
						
							|  |  |  |                 this.updateStreamingUI(content, isDone); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Update session data with additional metadata when streaming is complete
 | 
					
						
							|  |  |  |                 if (isDone) { | 
					
						
							|  |  |  |                     // Update our metadata with info from the server
 | 
					
						
							|  |  |  |                     server.get<{ | 
					
						
							|  |  |  |                         metadata?: { | 
					
						
							|  |  |  |                             model?: string; | 
					
						
							|  |  |  |                             provider?: string; | 
					
						
							|  |  |  |                             temperature?: number; | 
					
						
							|  |  |  |                             maxTokens?: number; | 
					
						
							|  |  |  |                             toolExecutions?: Array<{ | 
					
						
							|  |  |  |                                 id: string; | 
					
						
							|  |  |  |                                 name: string; | 
					
						
							|  |  |  |                                 arguments: any; | 
					
						
							|  |  |  |                                 result: any; | 
					
						
							|  |  |  |                                 error?: string; | 
					
						
							|  |  |  |                                 timestamp: string; | 
					
						
							|  |  |  |                             }>; | 
					
						
							|  |  |  |                             lastUpdated?: string; | 
					
						
							|  |  |  |                             usage?: { | 
					
						
							|  |  |  |                                 promptTokens?: number; | 
					
						
							|  |  |  |                                 completionTokens?: number; | 
					
						
							|  |  |  |                                 totalTokens?: number; | 
					
						
							|  |  |  |                             }; | 
					
						
							|  |  |  |                         }; | 
					
						
							|  |  |  |                         sources?: Array<{ | 
					
						
							|  |  |  |                             noteId: string; | 
					
						
							|  |  |  |                             title: string; | 
					
						
							|  |  |  |                             similarity?: number; | 
					
						
							|  |  |  |                             content?: string; | 
					
						
							|  |  |  |                         }>; | 
					
						
							| 
									
										
										
										
											2025-04-16 21:43:19 +00:00
										 |  |  |                     }>(`llm/sessions/${this.chatNoteId}`) | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                         .then((sessionData) => { | 
					
						
							|  |  |  |                             console.log("Got updated session data:", sessionData); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                             // Store metadata
 | 
					
						
							|  |  |  |                             if (sessionData.metadata) { | 
					
						
							|  |  |  |                                 this.metadata = { | 
					
						
							|  |  |  |                                     ...this.metadata, | 
					
						
							|  |  |  |                                     ...sessionData.metadata | 
					
						
							|  |  |  |                                 }; | 
					
						
							|  |  |  |                             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                             // Store sources
 | 
					
						
							|  |  |  |                             if (sessionData.sources && sessionData.sources.length > 0) { | 
					
						
							|  |  |  |                                 this.sources = sessionData.sources; | 
					
						
							|  |  |  |                                 this.showSources(sessionData.sources); | 
					
						
							|  |  |  |                             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                             // Make sure we include the cached tool executions
 | 
					
						
							|  |  |  |                             if (toolExecutionsCache.length > 0) { | 
					
						
							|  |  |  |                                 console.log(`Including ${toolExecutionsCache.length} cached tool executions in metadata`); | 
					
						
							|  |  |  |                                 if (!this.metadata.toolExecutions) { | 
					
						
							|  |  |  |                                     this.metadata.toolExecutions = []; | 
					
						
							|  |  |  |                                 } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                                 // Add any tool executions from our cache that aren't already in metadata
 | 
					
						
							|  |  |  |                                 const existingIds = new Set((this.metadata.toolExecutions || []).map((t: {id: string}) => t.id)); | 
					
						
							|  |  |  |                                 for (const toolExec of toolExecutionsCache) { | 
					
						
							|  |  |  |                                     if (!existingIds.has(toolExec.id)) { | 
					
						
							|  |  |  |                                         this.metadata.toolExecutions.push(toolExec); | 
					
						
							|  |  |  |                                         existingIds.add(toolExec.id); | 
					
						
							|  |  |  |                                     } | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                             // Save the updated data to the note
 | 
					
						
							|  |  |  |                             this.saveCurrentData() | 
					
						
							|  |  |  |                                 .catch(err => console.error("Failed to save data after streaming completed:", err)); | 
					
						
							|  |  |  |                         }) | 
					
						
							|  |  |  |                         .catch(err => console.error("Error fetching session data after streaming:", err)); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             }, | 
					
						
							|  |  |  |             // Thinking update handler
 | 
					
						
							|  |  |  |             (thinking: string) => { | 
					
						
							|  |  |  |                 this.showThinkingState(thinking); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             // Tool execution handler
 | 
					
						
							|  |  |  |             (toolData: any) => { | 
					
						
							|  |  |  |                 this.showToolExecutionInfo(toolData); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                 // Cache tools we see during streaming to include them in the final saved data
 | 
					
						
							|  |  |  |                 if (toolData && toolData.action === 'result' && toolData.tool) { | 
					
						
							|  |  |  |                     // Create a tool execution record
 | 
					
						
							|  |  |  |                     const toolExec = { | 
					
						
							|  |  |  |                         id: toolData.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, | 
					
						
							|  |  |  |                         name: toolData.tool, | 
					
						
							|  |  |  |                         arguments: toolData.args || {}, | 
					
						
							|  |  |  |                         result: toolData.result || {}, | 
					
						
							|  |  |  |                         error: toolData.error, | 
					
						
							|  |  |  |                         timestamp: new Date().toISOString() | 
					
						
							|  |  |  |                     }; | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Add to both our local cache for immediate saving and to metadata for later saving
 | 
					
						
							|  |  |  |                     toolExecutionsCache.push(toolExec); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Initialize toolExecutions array if it doesn't exist
 | 
					
						
							|  |  |  |                     if (!this.metadata.toolExecutions) { | 
					
						
							|  |  |  |                         this.metadata.toolExecutions = []; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Add tool execution to our metadata
 | 
					
						
							|  |  |  |                     this.metadata.toolExecutions.push(toolExec); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     console.log(`Cached tool execution for ${toolData.tool} to be saved later`); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |                     // Save immediately after receiving a tool execution
 | 
					
						
							|  |  |  |                     // This ensures we don't lose tool execution data if streaming fails
 | 
					
						
							|  |  |  |                     this.saveCurrentData().catch(err => { | 
					
						
							|  |  |  |                         console.error("Failed to save tool execution data:", err); | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             }, | 
					
						
							|  |  |  |             // Complete handler
 | 
					
						
							|  |  |  |             () => { | 
					
						
							|  |  |  |                 hideLoadingIndicator(this.loadingIndicator); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             // Error handler
 | 
					
						
							|  |  |  |             (error: Error) => { | 
					
						
							|  |  |  |                 this.handleError(error); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |      * Update the UI with streaming content | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2025-04-13 21:16:18 +00:00
										 |  |  |     private updateStreamingUI(assistantResponse: string, isDone: boolean = false) { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         // Get the existing assistant message or create a new one
 | 
					
						
							|  |  |  |         let assistantMessageEl = this.noteContextChatMessages.querySelector('.assistant-message:last-child'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!assistantMessageEl) { | 
					
						
							|  |  |  |             // If no assistant message yet, create one
 | 
					
						
							|  |  |  |             assistantMessageEl = document.createElement('div'); | 
					
						
							|  |  |  |             assistantMessageEl.className = 'assistant-message message mb-3'; | 
					
						
							|  |  |  |             this.noteContextChatMessages.appendChild(assistantMessageEl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add assistant profile icon
 | 
					
						
							|  |  |  |             const profileIcon = document.createElement('div'); | 
					
						
							|  |  |  |             profileIcon.className = 'profile-icon'; | 
					
						
							|  |  |  |             profileIcon.innerHTML = '<i class="bx bx-bot"></i>'; | 
					
						
							|  |  |  |             assistantMessageEl.appendChild(profileIcon); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add message content container
 | 
					
						
							|  |  |  |             const messageContent = document.createElement('div'); | 
					
						
							|  |  |  |             messageContent.className = 'message-content'; | 
					
						
							|  |  |  |             assistantMessageEl.appendChild(messageContent); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         // Update the content
 | 
					
						
							|  |  |  |         const messageContent = assistantMessageEl.querySelector('.message-content') as HTMLElement; | 
					
						
							|  |  |  |         messageContent.innerHTML = formatMarkdown(assistantResponse); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         // Apply syntax highlighting if this is the final update
 | 
					
						
							|  |  |  |         if (isDone) { | 
					
						
							|  |  |  |             applySyntaxHighlight($(assistantMessageEl as HTMLElement)); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Update message in the data model for storage
 | 
					
						
							|  |  |  |             const existingMsgIndex = this.messages.findIndex(msg => | 
					
						
							|  |  |  |                 msg.role === 'assistant' && msg.content !== assistantResponse | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             if (existingMsgIndex >= 0) { | 
					
						
							|  |  |  |                 // Update existing message
 | 
					
						
							|  |  |  |                 this.messages[existingMsgIndex].content = assistantResponse; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |                 // Add new message
 | 
					
						
							|  |  |  |                 this.messages.push({ | 
					
						
							|  |  |  |                     role: 'assistant', | 
					
						
							|  |  |  |                     content: assistantResponse | 
					
						
							|  |  |  |                 }); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Hide loading indicator
 | 
					
						
							|  |  |  |             hideLoadingIndicator(this.loadingIndicator); | 
					
						
							| 
									
										
										
										
											2025-04-13 21:43:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |             // Save the final state to the Chat Note
 | 
					
						
							|  |  |  |             this.saveCurrentData().catch(err => { | 
					
						
							|  |  |  |                 console.error("Failed to save assistant response to note:", err); | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 18:52:22 +00:00
										 |  |  |         // Scroll to bottom
 | 
					
						
							|  |  |  |         this.chatContainer.scrollTop = this.chatContainer.scrollHeight; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Handle general errors in the send message flow | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private handleError(error: Error) { | 
					
						
							|  |  |  |         hideLoadingIndicator(this.loadingIndicator); | 
					
						
							|  |  |  |         toastService.showError('Error sending message: ' + error.message); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private addMessageToChat(role: 'user' | 'assistant', content: string) { | 
					
						
							|  |  |  |         addMessageToChat(this.noteContextChatMessages, this.chatContainer, role, content); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private showSources(sources: Array<{noteId: string, title: string}>) { | 
					
						
							|  |  |  |         showSources( | 
					
						
							|  |  |  |             this.sourcesList, | 
					
						
							|  |  |  |             this.sourcesContainer, | 
					
						
							|  |  |  |             this.sourcesCount, | 
					
						
							|  |  |  |             sources, | 
					
						
							|  |  |  |             (noteId: string) => { | 
					
						
							|  |  |  |                 // Open the note in a new tab but don't switch to it
 | 
					
						
							|  |  |  |                 appContext.tabManager.openTabWithNoteWithHoisting(noteId, { activate: false }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private hideSources() { | 
					
						
							|  |  |  |         hideSources(this.sourcesContainer); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |      * Handle tool execution updates | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |      */ | 
					
						
							|  |  |  |     private showToolExecutionInfo(toolExecutionData: any) { | 
					
						
							|  |  |  |         console.log(`Showing tool execution info: ${JSON.stringify(toolExecutionData)}`); | 
					
						
							| 
									
										
										
										
											2025-04-15 22:34:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 20:15:14 +00:00
										 |  |  |         // Enhanced debugging for tool execution
 | 
					
						
							|  |  |  |         if (!toolExecutionData) { | 
					
						
							|  |  |  |             console.error('Tool execution data is missing or undefined'); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-15 22:34:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 20:15:14 +00:00
										 |  |  |         // Check for required properties
 | 
					
						
							|  |  |  |         const actionType = toolExecutionData.action || ''; | 
					
						
							|  |  |  |         const toolName = toolExecutionData.tool || 'unknown'; | 
					
						
							|  |  |  |         console.log(`Tool execution details: action=${actionType}, tool=${toolName}, hasResult=${!!toolExecutionData.result}`); | 
					
						
							| 
									
										
										
										
											2025-04-15 22:34:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 20:15:14 +00:00
										 |  |  |         // Force action to 'result' if missing but result is present
 | 
					
						
							|  |  |  |         if (!actionType && toolExecutionData.result) { | 
					
						
							|  |  |  |             console.log('Setting missing action to "result" since result is present'); | 
					
						
							|  |  |  |             toolExecutionData.action = 'result'; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |         // Create or get the tool execution container
 | 
					
						
							|  |  |  |         let toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution'); | 
					
						
							|  |  |  |         if (!toolExecutionElement) { | 
					
						
							|  |  |  |             toolExecutionElement = document.createElement('div'); | 
					
						
							|  |  |  |             toolExecutionElement.className = 'chat-tool-execution mb-3'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Create header with title and controls
 | 
					
						
							|  |  |  |             const header = document.createElement('div'); | 
					
						
							|  |  |  |             header.className = 'tool-execution-header d-flex align-items-center p-2 rounded'; | 
					
						
							|  |  |  |             header.innerHTML = `
 | 
					
						
							|  |  |  |                 <i class="bx bx-terminal me-2"></i> | 
					
						
							|  |  |  |                 <span class="flex-grow-1">Tool Execution</span> | 
					
						
							|  |  |  |                 <button type="button" class="btn btn-sm btn-link p-0 text-muted tool-execution-chat-clear" title="Clear tool execution history"> | 
					
						
							|  |  |  |                     <i class="bx bx-x"></i> | 
					
						
							|  |  |  |                 </button> | 
					
						
							|  |  |  |             `;
 | 
					
						
							|  |  |  |             toolExecutionElement.appendChild(header); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add click handler for clear button
 | 
					
						
							|  |  |  |             const clearButton = toolExecutionElement.querySelector('.tool-execution-chat-clear'); | 
					
						
							|  |  |  |             if (clearButton) { | 
					
						
							|  |  |  |                 clearButton.addEventListener('click', () => { | 
					
						
							|  |  |  |                     const stepsContainer = toolExecutionElement?.querySelector('.tool-execution-container'); | 
					
						
							|  |  |  |                     if (stepsContainer) { | 
					
						
							|  |  |  |                         stepsContainer.innerHTML = ''; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Create container for tool steps
 | 
					
						
							|  |  |  |             const stepsContainer = document.createElement('div'); | 
					
						
							|  |  |  |             stepsContainer.className = 'tool-execution-container p-2 rounded mb-2'; | 
					
						
							|  |  |  |             toolExecutionElement.appendChild(stepsContainer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Add to chat messages
 | 
					
						
							|  |  |  |             this.noteContextChatMessages.appendChild(toolExecutionElement); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get the steps container
 | 
					
						
							|  |  |  |         const stepsContainer = toolExecutionElement.querySelector('.tool-execution-container'); | 
					
						
							|  |  |  |         if (!stepsContainer) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Process based on action type
 | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |         const action = toolExecutionData.action || ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (action === 'start' || action === 'executing') { | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |             // Tool execution started
 | 
					
						
							|  |  |  |             const step = document.createElement('div'); | 
					
						
							|  |  |  |             step.className = 'tool-step executing p-2 mb-2 rounded'; | 
					
						
							|  |  |  |             step.innerHTML = `
 | 
					
						
							|  |  |  |                 <div class="d-flex align-items-center"> | 
					
						
							|  |  |  |                     <i class="bx bx-code-block me-2"></i> | 
					
						
							|  |  |  |                     <span>Executing tool: <strong>${toolExecutionData.tool || 'unknown'}</strong></span> | 
					
						
							|  |  |  |                 </div> | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |                 ${toolExecutionData.args ? `
 | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |                 <div class="tool-args mt-1 ps-3"> | 
					
						
							|  |  |  |                     <code>Args: ${JSON.stringify(toolExecutionData.args || {}, null, 2)}</code> | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |                 </div>` : ''}
 | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |             `;
 | 
					
						
							|  |  |  |             stepsContainer.appendChild(step); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |         else if (action === 'result' || action === 'complete') { | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |             // Tool execution completed with results
 | 
					
						
							|  |  |  |             const step = document.createElement('div'); | 
					
						
							|  |  |  |             step.className = 'tool-step result p-2 mb-2 rounded'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             let resultDisplay = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 20:15:14 +00:00
										 |  |  |             // Special handling for note search tools which have a specific structure
 | 
					
						
							|  |  |  |             if ((toolExecutionData.tool === 'search_notes' || toolExecutionData.tool === 'keyword_search_notes') && | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |                 typeof toolExecutionData.result === 'object' && | 
					
						
							|  |  |  |                 toolExecutionData.result.results) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 const results = toolExecutionData.result.results; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (results.length === 0) { | 
					
						
							|  |  |  |                     resultDisplay = `<div class="text-muted">No notes found matching the search criteria.</div>`; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     resultDisplay = `
 | 
					
						
							|  |  |  |                         <div class="search-results"> | 
					
						
							|  |  |  |                             <div class="mb-2">Found ${results.length} notes:</div> | 
					
						
							|  |  |  |                             <ul class="list-unstyled ps-1"> | 
					
						
							|  |  |  |                                 ${results.map((note: any) => `
 | 
					
						
							|  |  |  |                                     <li class="mb-1"> | 
					
						
							|  |  |  |                                         <a href="#" class="note-link" data-note-id="${note.noteId}">${note.title}</a> | 
					
						
							|  |  |  |                                         ${note.similarity < 1 ? `<span class="text-muted small ms-1">(similarity: ${(note.similarity * 100).toFixed(0)}%)</span>` : ''} | 
					
						
							|  |  |  |                                     </li> | 
					
						
							|  |  |  |                                 `).join('')}
 | 
					
						
							|  |  |  |                             </ul> | 
					
						
							|  |  |  |                         </div> | 
					
						
							|  |  |  |                     `;
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Format the result based on type for other tools
 | 
					
						
							|  |  |  |             else if (typeof toolExecutionData.result === 'object') { | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |                 // For objects, format as pretty JSON
 | 
					
						
							|  |  |  |                 resultDisplay = `<pre class="mb-0"><code>${JSON.stringify(toolExecutionData.result, null, 2)}</code></pre>`; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // For simple values, display as text
 | 
					
						
							|  |  |  |                 resultDisplay = `<div>${String(toolExecutionData.result)}</div>`; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             step.innerHTML = `
 | 
					
						
							|  |  |  |                 <div class="d-flex align-items-center"> | 
					
						
							|  |  |  |                     <i class="bx bx-terminal me-2"></i> | 
					
						
							|  |  |  |                     <span>Tool: <strong>${toolExecutionData.tool || 'unknown'}</strong></span> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |                 <div class="tool-result mt-1 ps-3"> | 
					
						
							|  |  |  |                     ${resultDisplay} | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             `;
 | 
					
						
							|  |  |  |             stepsContainer.appendChild(step); | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 20:15:14 +00:00
										 |  |  |             // Add event listeners for note links if this is a note search result
 | 
					
						
							|  |  |  |             if (toolExecutionData.tool === 'search_notes' || toolExecutionData.tool === 'keyword_search_notes') { | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |                 const noteLinks = step.querySelectorAll('.note-link'); | 
					
						
							|  |  |  |                 noteLinks.forEach(link => { | 
					
						
							|  |  |  |                     link.addEventListener('click', (e) => { | 
					
						
							|  |  |  |                         e.preventDefault(); | 
					
						
							|  |  |  |                         const noteId = (e.currentTarget as HTMLElement).getAttribute('data-note-id'); | 
					
						
							|  |  |  |                         if (noteId) { | 
					
						
							|  |  |  |                             // Open the note in a new tab but don't switch to it
 | 
					
						
							|  |  |  |                             appContext.tabManager.openTabWithNoteWithHoisting(noteId, { activate: false }); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |         else if (action === 'error') { | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  |             // Tool execution failed
 | 
					
						
							|  |  |  |             const step = document.createElement('div'); | 
					
						
							|  |  |  |             step.className = 'tool-step error p-2 mb-2 rounded'; | 
					
						
							|  |  |  |             step.innerHTML = `
 | 
					
						
							|  |  |  |                 <div class="d-flex align-items-center"> | 
					
						
							|  |  |  |                     <i class="bx bx-error-circle me-2"></i> | 
					
						
							|  |  |  |                     <span>Error in tool: <strong>${toolExecutionData.tool || 'unknown'}</strong></span> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |                 <div class="tool-error mt-1 ps-3 text-danger"> | 
					
						
							|  |  |  |                     ${toolExecutionData.error || 'Unknown error'} | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             `;
 | 
					
						
							|  |  |  |             stepsContainer.appendChild(step); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-13 20:12:17 +00:00
										 |  |  |         else if (action === 'generating') { | 
					
						
							|  |  |  |             // Generating final response with tool results
 | 
					
						
							|  |  |  |             const step = document.createElement('div'); | 
					
						
							|  |  |  |             step.className = 'tool-step generating p-2 mb-2 rounded'; | 
					
						
							|  |  |  |             step.innerHTML = `
 | 
					
						
							|  |  |  |                 <div class="d-flex align-items-center"> | 
					
						
							|  |  |  |                     <i class="bx bx-message-dots me-2"></i> | 
					
						
							|  |  |  |                     <span>Generating response with tool results...</span> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             `;
 | 
					
						
							|  |  |  |             stepsContainer.appendChild(step); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Make sure the loading indicator is shown during tool execution
 | 
					
						
							|  |  |  |         this.loadingIndicator.style.display = 'flex'; | 
					
						
							| 
									
										
										
										
											2025-04-12 17:23:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Scroll the chat container to show the tool execution
 | 
					
						
							|  |  |  |         this.chatContainer.scrollTop = this.chatContainer.scrollHeight; | 
					
						
							| 
									
										
										
										
											2025-04-11 21:52:54 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Show thinking state in the UI | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private showThinkingState(thinkingData: string) { | 
					
						
							|  |  |  |         // Thinking state is now updated via the in-chat UI in updateStreamingUI
 | 
					
						
							|  |  |  |         // This method is now just a hook for the WebSocket handlers
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Show the loading indicator
 | 
					
						
							|  |  |  |         this.loadingIndicator.style.display = 'flex'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private initializeEventListeners() { | 
					
						
							|  |  |  |         this.noteContextChatForm.addEventListener('submit', (e) => { | 
					
						
							|  |  |  |             e.preventDefault(); | 
					
						
							|  |  |  |             const content = this.noteContextChatInput.value; | 
					
						
							|  |  |  |             this.sendMessage(content); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add auto-resize functionality to the textarea
 | 
					
						
							|  |  |  |         this.noteContextChatInput.addEventListener('input', () => { | 
					
						
							|  |  |  |             this.noteContextChatInput.style.height = 'auto'; | 
					
						
							|  |  |  |             this.noteContextChatInput.style.height = `${this.noteContextChatInput.scrollHeight}px`; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Handle Enter key (send on Enter, new line on Shift+Enter)
 | 
					
						
							|  |  |  |         this.noteContextChatInput.addEventListener('keydown', (e) => { | 
					
						
							|  |  |  |             if (e.key === 'Enter' && !e.shiftKey) { | 
					
						
							|  |  |  |                 e.preventDefault(); | 
					
						
							|  |  |  |                 this.noteContextChatForm.dispatchEvent(new Event('submit')); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |