mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	clean up silly chat_widget that was in the wrong place
This commit is contained in:
		| @@ -1,9 +1,3 @@ | |||||||
| import options from "../../../services/options.js"; |  | ||||||
| import ChatWidget from "../widgets/llm/chat_widget.js"; |  | ||||||
| import TabContext from "../widgets/right_panel_tabs.js"; |  | ||||||
| import rightPaneTabManager from "./right_pane_tab_manager.js"; |  | ||||||
| import keyboardActionsService from "./keyboard_actions.js"; |  | ||||||
|  |  | ||||||
| function initComponents() { | function initComponents() { | ||||||
|     // ... existing code ... |     // ... existing code ... | ||||||
|  |  | ||||||
| @@ -12,50 +6,6 @@ function initComponents() { | |||||||
|     // ... existing code ... |     // ... existing code ... | ||||||
| } | } | ||||||
|  |  | ||||||
| function addChatTab() { |  | ||||||
|     if (!options.getOptionBool('aiEnabled')) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const chatWidget = new ChatWidget(); |  | ||||||
|  |  | ||||||
|     // Add chat widget to the right pane |  | ||||||
|     const chatTab = new TabContext("AI Chat", chatWidget); |  | ||||||
|     chatTab.renderTitle = title => { |  | ||||||
|         return $(`<span class="tab-title"><span class="bx bx-bot"></span> ${title}</span>`); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     rightPaneTabManager.addTabContext(chatTab); |  | ||||||
|  |  | ||||||
|     // Add chat button to the global menu |  | ||||||
|     const $button = $('<button class="button-widget global-menu-button" title="Open AI Chat (Ctrl+Shift+C)"><span class="bx bx-chat"></span></button>'); |  | ||||||
|  |  | ||||||
|     $button.on('click', () => { |  | ||||||
|         chatTab.activate(); |  | ||||||
|         chatWidget.refresh(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     $button.insertBefore($('.global-menu-button:first')); // Add to the beginning of global menu |  | ||||||
|  |  | ||||||
|     // Add keyboard shortcut |  | ||||||
|     keyboardActionsService.setupActionsForScope('window', { |  | ||||||
|         'openAiChat': { |  | ||||||
|             'enabled': true, |  | ||||||
|             'title': 'Open AI Chat', |  | ||||||
|             'clickNote': true, |  | ||||||
|             'shortcutKeys': { |  | ||||||
|                 'keyCode': 'C', |  | ||||||
|                 'ctrlKey': true, |  | ||||||
|                 'shiftKey': true |  | ||||||
|             }, |  | ||||||
|             'handler': () => { |  | ||||||
|                 chatTab.activate(); |  | ||||||
|                 chatWidget.refresh(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Export the functions to make them available to other modules | // Export the functions to make them available to other modules | ||||||
| export default { | export default { | ||||||
|     initComponents, |     initComponents, | ||||||
|   | |||||||
| @@ -1,365 +0,0 @@ | |||||||
| import TabAwareWidget from "../tab_aware_widget.js"; |  | ||||||
| import chatService from "../../../../services/llm/chat_service.js"; |  | ||||||
| import options from "../../services/options.js"; |  | ||||||
| import utils from "../../services/utils.js"; |  | ||||||
|  |  | ||||||
| const TPL = ` |  | ||||||
| <div class="chat-widget"> |  | ||||||
|     <div class="chat-header"> |  | ||||||
|         <div class="chat-title"></div> |  | ||||||
|         <div class="chat-actions"> |  | ||||||
|             <button class="btn btn-sm chat-new-btn" title="New Chat"> |  | ||||||
|                 <span class="bx bx-plus"></span> |  | ||||||
|             </button> |  | ||||||
|             <button class="btn btn-sm chat-options-btn" title="Chat Options"> |  | ||||||
|                 <span class="bx bx-cog"></span> |  | ||||||
|             </button> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
|     <div class="chat-messages"></div> |  | ||||||
|     <div class="chat-controls"> |  | ||||||
|         <div class="chat-input-container"> |  | ||||||
|             <textarea class="chat-input form-control" placeholder="Type your message here..." rows="2"></textarea> |  | ||||||
|         </div> |  | ||||||
|         <div class="chat-buttons"> |  | ||||||
|             <button class="btn btn-primary btn-sm chat-send-btn" title="Send Message"> |  | ||||||
|                 <span class="bx bx-send"></span> |  | ||||||
|             </button> |  | ||||||
|             <button class="btn btn-outline-secondary btn-sm chat-add-context-btn" title="Add current note as context"> |  | ||||||
|                 <span class="bx bx-link"></span> |  | ||||||
|             </button> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| </div> |  | ||||||
| `; |  | ||||||
|  |  | ||||||
| const MESSAGE_TPL = ` |  | ||||||
| <div class="chat-message"> |  | ||||||
|     <div class="chat-message-avatar"> |  | ||||||
|         <span class="bx"></span> |  | ||||||
|     </div> |  | ||||||
|     <div class="chat-message-content"></div> |  | ||||||
| </div> |  | ||||||
| `; |  | ||||||
|  |  | ||||||
| export default class ChatWidget extends TabAwareWidget { |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|  |  | ||||||
|         this.activeSessionId = null; |  | ||||||
|         this.$widget = $(TPL); |  | ||||||
|         this.$title = this.$widget.find('.chat-title'); |  | ||||||
|         this.$messagesContainer = this.$widget.find('.chat-messages'); |  | ||||||
|         this.$input = this.$widget.find('.chat-input'); |  | ||||||
|         this.$sendBtn = this.$widget.find('.chat-send-btn'); |  | ||||||
|         this.$newChatBtn = this.$widget.find('.chat-new-btn'); |  | ||||||
|         this.$optionsBtn = this.$widget.find('.chat-options-btn'); |  | ||||||
|         this.$addContextBtn = this.$widget.find('.chat-add-context-btn'); |  | ||||||
|  |  | ||||||
|         this.initialized = false; |  | ||||||
|         this.isActiveTab = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     isEnabled() { |  | ||||||
|         return options.getOptionBool('aiEnabled'); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     doRender() { |  | ||||||
|         this.$widget.on('click', '.chat-send-btn', async () => { |  | ||||||
|             if (!this.activeSessionId) return; |  | ||||||
|  |  | ||||||
|             const message = this.$input.val().trim(); |  | ||||||
|             if (!message) return; |  | ||||||
|  |  | ||||||
|             this.$input.val(''); |  | ||||||
|             this.$input.prop('disabled', true); |  | ||||||
|             this.$sendBtn.prop('disabled', true); |  | ||||||
|  |  | ||||||
|             await this.sendMessage(message); |  | ||||||
|  |  | ||||||
|             this.$input.prop('disabled', false); |  | ||||||
|             this.$sendBtn.prop('disabled', false); |  | ||||||
|             this.$input.focus(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.$input.on('keydown', async e => { |  | ||||||
|             if (e.key === 'Enter' && !e.shiftKey) { |  | ||||||
|                 e.preventDefault(); |  | ||||||
|                 this.$sendBtn.click(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.$newChatBtn.on('click', async () => { |  | ||||||
|             await this.startNewChat(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.$addContextBtn.on('click', async () => { |  | ||||||
|             if (!this.activeSessionId || !this.noteId) return; |  | ||||||
|  |  | ||||||
|             this.$input.prop('disabled', true); |  | ||||||
|             this.$sendBtn.prop('disabled', true); |  | ||||||
|             this.$addContextBtn.prop('disabled', true); |  | ||||||
|  |  | ||||||
|             await this.addNoteContext(); |  | ||||||
|  |  | ||||||
|             this.$input.prop('disabled', false); |  | ||||||
|             this.$sendBtn.prop('disabled', false); |  | ||||||
|             this.$addContextBtn.prop('disabled', false); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.$optionsBtn.on('click', () => { |  | ||||||
|             this.triggerEvent('openOptions'); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         return this.$widget; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async refresh() { |  | ||||||
|         if (!this.isEnabled()) { |  | ||||||
|             this.toggleVisibility(false); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.toggleVisibility(true); |  | ||||||
|  |  | ||||||
|         if (!this.initialized) { |  | ||||||
|             await this.initialize(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (this.activeSessionId) { |  | ||||||
|             await this.loadSession(this.activeSessionId); |  | ||||||
|         } else { |  | ||||||
|             await this.startNewChat(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     toggleVisibility(show) { |  | ||||||
|         this.$widget.toggleClass('d-none', !show); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async initialize() { |  | ||||||
|         // Load last or create new chat session |  | ||||||
|         const sessions = await chatService.getAllSessions(); |  | ||||||
|  |  | ||||||
|         if (sessions.length > 0) { |  | ||||||
|             // Use the most recent session |  | ||||||
|             this.activeSessionId = sessions[0].id; |  | ||||||
|             await this.loadSession(this.activeSessionId); |  | ||||||
|         } else { |  | ||||||
|             await this.startNewChat(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.initialized = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async loadSession(sessionId) { |  | ||||||
|         try { |  | ||||||
|             const session = await chatService.getOrCreateSession(sessionId); |  | ||||||
|             this.activeSessionId = session.id; |  | ||||||
|  |  | ||||||
|             // Update title |  | ||||||
|             this.$title.text(session.title || 'New Chat'); |  | ||||||
|  |  | ||||||
|             // Clear and reload messages |  | ||||||
|             this.$messagesContainer.empty(); |  | ||||||
|  |  | ||||||
|             for (const message of session.messages) { |  | ||||||
|                 this.addMessageToUI(message); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Scroll to bottom |  | ||||||
|             this.scrollToBottom(); |  | ||||||
|  |  | ||||||
|         } catch (error) { |  | ||||||
|             console.error('Failed to load chat session:', error); |  | ||||||
|             await this.startNewChat(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async startNewChat() { |  | ||||||
|         try { |  | ||||||
|             const session = await chatService.createSession(); |  | ||||||
|             this.activeSessionId = session.id; |  | ||||||
|  |  | ||||||
|             // Update title |  | ||||||
|             this.$title.text(session.title || 'New Chat'); |  | ||||||
|  |  | ||||||
|             // Clear messages |  | ||||||
|             this.$messagesContainer.empty(); |  | ||||||
|  |  | ||||||
|             // Add welcome message |  | ||||||
|             const welcomeMessage = { |  | ||||||
|                 role: 'assistant', |  | ||||||
|                 content: 'Hello! How can I assist you today?' |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             this.addMessageToUI(welcomeMessage); |  | ||||||
|  |  | ||||||
|             // Focus input |  | ||||||
|             this.$input.focus(); |  | ||||||
|  |  | ||||||
|         } catch (error) { |  | ||||||
|             console.error('Failed to create new chat session:', error); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async sendMessage(content) { |  | ||||||
|         if (!this.activeSessionId) return; |  | ||||||
|  |  | ||||||
|         // Add user message to UI immediately |  | ||||||
|         const userMessage = { role: 'user', content }; |  | ||||||
|         this.addMessageToUI(userMessage); |  | ||||||
|  |  | ||||||
|         // Prepare for streaming response |  | ||||||
|         const $assistantMessage = this.createEmptyAssistantMessage(); |  | ||||||
|  |  | ||||||
|         // Send to service with streaming callback |  | ||||||
|         try { |  | ||||||
|             await chatService.sendMessage( |  | ||||||
|                 this.activeSessionId, |  | ||||||
|                 content, |  | ||||||
|                 null, |  | ||||||
|                 (content, isDone) => { |  | ||||||
|                     // Update the message content as it streams |  | ||||||
|                     $assistantMessage.find('.chat-message-content').html(this.formatMessageContent(content)); |  | ||||||
|                     this.scrollToBottom(); |  | ||||||
|  |  | ||||||
|                     if (isDone) { |  | ||||||
|                         // Update session title if it changed |  | ||||||
|                         chatService.getOrCreateSession(this.activeSessionId).then(session => { |  | ||||||
|                             this.$title.text(session.title); |  | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             ); |  | ||||||
|         } catch (error) { |  | ||||||
|             console.error('Error sending message:', error); |  | ||||||
|  |  | ||||||
|             // Show error in UI if not already shown by streaming |  | ||||||
|             $assistantMessage.find('.chat-message-content').html( |  | ||||||
|                 this.formatMessageContent(`Error: Failed to generate response. ${error.message || 'Please check AI settings and try again.'}`) |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.scrollToBottom(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async addNoteContext() { |  | ||||||
|         if (!this.activeSessionId || !this.noteId) return; |  | ||||||
|  |  | ||||||
|         try { |  | ||||||
|             // Show loading message |  | ||||||
|             const $loadingMessage = this.createEmptyAssistantMessage(); |  | ||||||
|             $loadingMessage.find('.chat-message-content').html('Loading note context...'); |  | ||||||
|  |  | ||||||
|             await chatService.addNoteContext(this.activeSessionId, this.noteId); |  | ||||||
|  |  | ||||||
|             // Remove loading message |  | ||||||
|             $loadingMessage.remove(); |  | ||||||
|  |  | ||||||
|             // Reload the session to show updated messages |  | ||||||
|             await this.loadSession(this.activeSessionId); |  | ||||||
|  |  | ||||||
|         } catch (error) { |  | ||||||
|             console.error('Error adding note context:', error); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     addMessageToUI(message) { |  | ||||||
|         const $message = $(MESSAGE_TPL); |  | ||||||
|  |  | ||||||
|         // Set avatar icon based on role |  | ||||||
|         if (message.role === 'user') { |  | ||||||
|             $message.addClass('chat-message-user'); |  | ||||||
|             $message.find('.chat-message-avatar .bx').addClass('bx-user'); |  | ||||||
|         } else { |  | ||||||
|             $message.addClass('chat-message-assistant'); |  | ||||||
|             $message.find('.chat-message-avatar .bx').addClass('bx-bot'); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Set content |  | ||||||
|         $message.find('.chat-message-content').html(this.formatMessageContent(message.content)); |  | ||||||
|  |  | ||||||
|         // Add to container |  | ||||||
|         this.$messagesContainer.append($message); |  | ||||||
|  |  | ||||||
|         // Scroll to bottom |  | ||||||
|         this.scrollToBottom(); |  | ||||||
|  |  | ||||||
|         return $message; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     createEmptyAssistantMessage() { |  | ||||||
|         const $message = $(MESSAGE_TPL); |  | ||||||
|         $message.addClass('chat-message-assistant'); |  | ||||||
|         $message.find('.chat-message-avatar .bx').addClass('bx-bot'); |  | ||||||
|         $message.find('.chat-message-content').html('<div class="chat-loading">●●●</div>'); |  | ||||||
|  |  | ||||||
|         this.$messagesContainer.append($message); |  | ||||||
|         this.scrollToBottom(); |  | ||||||
|  |  | ||||||
|         return $message; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     formatMessageContent(content) { |  | ||||||
|         if (!content) return ''; |  | ||||||
|  |  | ||||||
|         // First extract code blocks to protect them from HTML escaping |  | ||||||
|         const codeBlocks = []; |  | ||||||
|         let processedContent = content.replace(/```(\w+)?\n([\s\S]+?)\n```/g, (match, language, code) => { |  | ||||||
|             const placeholder = `__CODE_BLOCK_${codeBlocks.length}__`; |  | ||||||
|             const languageClass = language ? ` language-${language}` : ''; |  | ||||||
|             codeBlocks.push(`<pre class="code${languageClass}"><code>${utils.escapeHtml(code)}</code></pre>`); |  | ||||||
|             return placeholder; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Escape HTML in the remaining content |  | ||||||
|         processedContent = utils.escapeHtml(processedContent); |  | ||||||
|  |  | ||||||
|         // Convert inline code - look for backticks that weren't part of a code block |  | ||||||
|         processedContent = processedContent.replace(/`([^`]+)`/g, '<code>$1</code>'); |  | ||||||
|  |  | ||||||
|         // Convert line breaks |  | ||||||
|         processedContent = processedContent.replace(/\n/g, '<br>'); |  | ||||||
|  |  | ||||||
|         // Restore code blocks |  | ||||||
|         codeBlocks.forEach((block, index) => { |  | ||||||
|             processedContent = processedContent.replace(`__CODE_BLOCK_${index}__`, block); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         return processedContent; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     scrollToBottom() { |  | ||||||
|         this.$messagesContainer.scrollTop(this.$messagesContainer[0].scrollHeight); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @param {string} noteId |  | ||||||
|      */ |  | ||||||
|     async noteSwitched(noteId) { |  | ||||||
|         this.noteId = noteId; |  | ||||||
|  |  | ||||||
|         if (this.isActiveTab) { |  | ||||||
|             // Only refresh if we're the active tab |  | ||||||
|             await this.refresh(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @param {boolean} active |  | ||||||
|      */ |  | ||||||
|     activeTabChanged(active) { |  | ||||||
|         this.isActiveTab = active; |  | ||||||
|  |  | ||||||
|         if (active) { |  | ||||||
|             this.refresh(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     entitiesReloaded() { |  | ||||||
|         if (this.isActiveTab) { |  | ||||||
|             this.refresh(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user