From 16604c85a6ce59b8958100aac60c98bf9d8d5293 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Fri, 11 Apr 2025 21:38:10 +0000 Subject: [PATCH] I think this is close --- src/public/app/widgets/llm_chat_panel.ts | 631 ++++++++++++++--------- src/public/stylesheets/llm_chat.css | 76 +++ 2 files changed, 468 insertions(+), 239 deletions(-) diff --git a/src/public/app/widgets/llm_chat_panel.ts b/src/public/app/widgets/llm_chat_panel.ts index 90f40c42f..be0041c8a 100644 --- a/src/public/app/widgets/llm_chat_panel.ts +++ b/src/public/app/widgets/llm_chat_panel.ts @@ -27,16 +27,6 @@ const TPL = ` Loading... ${t('ai_llm.agent.processing')} - @@ -98,8 +88,6 @@ export default class LlmChatPanel extends BasicWidget { private noteContextChatSendButton!: HTMLButtonElement; private chatContainer!: HTMLElement; private loadingIndicator!: HTMLElement; - private toolExecutionInfo!: HTMLElement; - private toolExecutionSteps!: HTMLElement; private sourcesList!: HTMLElement; private useAdvancedContextCheckbox!: HTMLInputElement; private showThinkingCheckbox!: HTMLInputElement; @@ -157,8 +145,6 @@ export default class LlmChatPanel extends BasicWidget { 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.toolExecutionInfo = element.querySelector('.tool-execution-info') as HTMLElement; - this.toolExecutionSteps = element.querySelector('.tool-execution-steps') as HTMLElement; this.sourcesList = element.querySelector('.sources-list') as HTMLElement; this.useAdvancedContextCheckbox = element.querySelector('.use-advanced-context-checkbox') as HTMLInputElement; this.showThinkingCheckbox = element.querySelector('.show-thinking-checkbox') as HTMLInputElement; @@ -210,12 +196,16 @@ export default class LlmChatPanel extends BasicWidget { } try { + // Extract current tool execution steps if any exist + const toolSteps = this.extractInChatToolSteps(); + const dataToSave = { messages: this.messages, - sessionId: this.sessionId + sessionId: this.sessionId, + toolSteps: toolSteps // Save tool execution steps alongside messages }; - console.log(`Saving chat data with sessionId: ${this.sessionId}`); + console.log(`Saving chat data with sessionId: ${this.sessionId} and ${toolSteps.length} tool steps`); await this.onSaveData(dataToSave); } catch (error) { @@ -223,6 +213,61 @@ export default class LlmChatPanel extends BasicWidget { } } + /** + * Extract tool execution steps from the DOM that are within the chat flow + */ + private extractInChatToolSteps(): Array<{type: string, name?: string, content: string}> { + const steps: Array<{type: string, name?: string, content: string}> = []; + + // Look for tool execution in the chat flow + const toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution'); + + if (toolExecutionElement) { + // Find all tool step elements + const stepElements = toolExecutionElement.querySelectorAll('.tool-step'); + + stepElements.forEach(stepEl => { + const stepHtml = stepEl.innerHTML; + + // Determine the step type based on icons or classes present + let type = 'info'; + let name: string | undefined; + let content = ''; + + if (stepHtml.includes('bx-code-block')) { + type = 'executing'; + content = 'Executing tools...'; + } else if (stepHtml.includes('bx-terminal')) { + type = 'result'; + // Extract the tool name from the step + const nameMatch = stepHtml.match(/]*>Tool: ([^<]+)<\/span>/); + name = nameMatch ? nameMatch[1] : 'unknown'; + + // Extract the content from the div with class mt-1 ps-3 + const contentEl = stepEl.querySelector('.mt-1.ps-3'); + content = contentEl ? contentEl.innerHTML : ''; + } else if (stepHtml.includes('bx-error-circle')) { + type = 'error'; + const nameMatch = stepHtml.match(/]*>Tool: ([^<]+)<\/span>/); + name = nameMatch ? nameMatch[1] : 'unknown'; + + const contentEl = stepEl.querySelector('.mt-1.ps-3.text-danger'); + content = contentEl ? contentEl.innerHTML : ''; + } else if (stepHtml.includes('bx-message-dots')) { + type = 'generating'; + content = 'Generating response with tool results...'; + } else if (stepHtml.includes('bx-loader-alt')) { + // Skip the initializing spinner + return; + } + + steps.push({ type, name, content }); + }); + } + + return steps; + } + /** * Load saved chat data from the note attribute */ @@ -246,6 +291,12 @@ export default class LlmChatPanel extends BasicWidget { 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); + } + // Load session ID if available if (savedData.sessionId) { try { @@ -261,7 +312,7 @@ export default class LlmChatPanel extends BasicWidget { await this.createChatSession(); } } catch (error) { - console.log(`Error checking saved session ${savedData.sessionId}, will create new one`); + console.log(`Error checking saved session ${savedData.sessionId}, creating a new one`); this.sessionId = null; await this.createChatSession(); } @@ -280,6 +331,54 @@ export default class LlmChatPanel extends BasicWidget { return false; } + /** + * Restore tool execution steps in the chat UI + */ + private restoreInChatToolSteps(steps: Array<{type: string, name?: string, content: string}>) { + 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 = ` +
+
+
+ + Tool Execution +
+ +
+
+ ${this.renderToolStepsHtml(steps)} +
+
+ `; + + // 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(); + }); + } + } + async refresh() { if (!this.isVisible()) { return; @@ -731,62 +830,135 @@ export default class LlmChatPanel extends BasicWidget { return; } - // Check if we already have an assistant message element to update - const assistantElement = this.noteContextChatMessages.querySelector('.assistant-message:last-child .message-content'); + // Extract the tool execution steps and final response + const toolSteps = this.extractToolExecutionSteps(assistantResponse); + const finalResponseText = this.extractFinalResponse(assistantResponse); - if (assistantElement) { - console.log(`[${logId}] Found existing assistant message element, updating content`); - try { - // Format markdown and update the element - const formattedContent = this.formatMarkdown(assistantResponse); + // Find existing assistant message or create one if needed + let assistantElement = this.noteContextChatMessages.querySelector('.assistant-message:last-child .message-content'); - // Ensure content is properly formatted - if (!formattedContent || formattedContent.trim() === '') { - console.warn(`[${logId}] Formatted content is empty, using original content`); - assistantElement.textContent = assistantResponse; + // First, check if we need to add the tool execution steps to the chat flow + if (toolSteps.length > 0) { + // Look for an existing tool execution element in the chat flow + let toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution'); + + if (!toolExecutionElement) { + // Create a new tool execution element in the chat flow + // Place it right before the assistant message if it exists, or at the end of chat + toolExecutionElement = document.createElement('div'); + toolExecutionElement.className = 'chat-tool-execution mb-3'; + + // If there's an assistant message, insert before it + const assistantMessage = this.noteContextChatMessages.querySelector('.assistant-message:last-child'); + if (assistantMessage) { + this.noteContextChatMessages.insertBefore(toolExecutionElement, assistantMessage); } else { - assistantElement.innerHTML = formattedContent; - } - - // Apply syntax highlighting to any code blocks in the updated content - applySyntaxHighlight($(assistantElement as HTMLElement)); - - console.log(`[${logId}] Successfully updated existing element with ${formattedContent.length} chars of HTML`); - } catch (err) { - console.error(`[${logId}] Error updating existing element:`, err); - // Fallback to text content if HTML update fails - try { - assistantElement.textContent = assistantResponse; - console.log(`[${logId}] Fallback to text content successful`); - } catch (fallbackErr) { - console.error(`[${logId}] Even fallback update failed:`, fallbackErr); + // Otherwise append to the end + this.noteContextChatMessages.appendChild(toolExecutionElement); } } - } else { - console.log(`[${logId}] No existing assistant message element found, creating new one`); - try { - this.addMessageToChat('assistant', assistantResponse); - console.log(`[${logId}] Successfully added new assistant message`); - } catch (err) { - console.error(`[${logId}] Error adding new message:`, err); - // Last resort emergency approach - create element directly + // Update the tool execution content + toolExecutionElement.innerHTML = ` +
+
+
+ + Tool Execution +
+ +
+
+ ${this.renderToolStepsHtml(toolSteps)} +
+
+ `; + + // 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(); + }); + } + } + + // Now update or create the assistant message with the final response + if (finalResponseText) { + if (assistantElement) { + console.log(`[${logId}] Found existing assistant message element, updating with final response`); try { - console.log(`[${logId}] Attempting emergency DOM update`); - const emergencyElement = document.createElement('div'); - emergencyElement.className = 'chat-message assistant-message mb-3 d-flex'; - emergencyElement.innerHTML = ` -
- -
-
- ${assistantResponse} -
- `; - this.noteContextChatMessages.appendChild(emergencyElement); - console.log(`[${logId}] Emergency DOM update successful`); - } catch (emergencyErr) { - console.error(`[${logId}] Emergency DOM update failed:`, emergencyErr); + // Format the final response with markdown + const formattedResponse = this.formatMarkdown(finalResponseText); + + // Update the content + assistantElement.innerHTML = formattedResponse || ''; + + // Apply syntax highlighting to any code blocks in the updated content + applySyntaxHighlight($(assistantElement as HTMLElement)); + + console.log(`[${logId}] Successfully updated existing element with final response`); + } catch (err) { + console.error(`[${logId}] Error updating existing element:`, err); + // Fallback to text content if HTML update fails + try { + assistantElement.textContent = finalResponseText; + console.log(`[${logId}] Fallback to text content successful`); + } catch (fallbackErr) { + console.error(`[${logId}] Even fallback update failed:`, fallbackErr); + } + } + } else { + console.log(`[${logId}] No existing assistant message element found, creating new one`); + try { + // Create new message element + const messageElement = document.createElement('div'); + messageElement.className = 'chat-message assistant-message mb-3 d-flex'; + + const avatarElement = document.createElement('div'); + avatarElement.className = 'message-avatar d-flex align-items-center justify-content-center me-2 assistant-avatar'; + avatarElement.innerHTML = ''; + + const contentElement = document.createElement('div'); + contentElement.className = 'message-content p-3 rounded flex-grow-1 assistant-content'; + + // Only show the final response in the message content + contentElement.innerHTML = this.formatMarkdown(finalResponseText) || ''; + + messageElement.appendChild(avatarElement); + messageElement.appendChild(contentElement); + + this.noteContextChatMessages.appendChild(messageElement); + + // Apply syntax highlighting to any code blocks in the message + applySyntaxHighlight($(contentElement)); + + console.log(`[${logId}] Successfully added new assistant message`); + } catch (err) { + console.error(`[${logId}] Error adding new message:`, err); + + // Last resort emergency approach - create element directly + try { + console.log(`[${logId}] Attempting emergency DOM update`); + const emergencyElement = document.createElement('div'); + emergencyElement.className = 'chat-message assistant-message mb-3 d-flex'; + emergencyElement.innerHTML = ` +
+ +
+
+ ${finalResponseText} +
+ `; + this.noteContextChatMessages.appendChild(emergencyElement); + console.log(`[${logId}] Emergency DOM update successful`); + } catch (emergencyErr) { + console.error(`[${logId}] Emergency DOM update failed:`, emergencyErr); + } } } } @@ -802,6 +974,131 @@ export default class LlmChatPanel extends BasicWidget { } } + /** + * Render tool steps as HTML for display in chat + */ + private renderToolStepsHtml(steps: Array<{type: string, name?: string, content: string}>): string { + if (!steps || steps.length === 0) return ''; + + let html = ''; + + steps.forEach(step => { + let icon, labelClass, content; + + switch (step.type) { + case 'executing': + icon = 'bx-code-block text-primary'; + labelClass = ''; + content = `
+ + ${step.content} +
`; + break; + + case 'result': + icon = 'bx-terminal text-success'; + labelClass = 'fw-bold'; + content = `
+ + Tool: ${step.name || 'unknown'} +
+
${step.content}
`; + break; + + case 'error': + icon = 'bx-error-circle text-danger'; + labelClass = 'fw-bold text-danger'; + content = `
+ + Tool: ${step.name || 'unknown'} +
+
${step.content}
`; + break; + + case 'generating': + icon = 'bx-message-dots text-info'; + labelClass = ''; + content = `
+ + ${step.content} +
`; + break; + + default: + icon = 'bx-info-circle text-muted'; + labelClass = ''; + content = `
+ + ${step.content} +
`; + } + + html += `
${content}
`; + }); + + return html; + } + + /** + * Extract tool execution steps from the response + */ + private extractToolExecutionSteps(content: string): Array<{type: string, name?: string, content: string}> { + if (!content) return []; + + const steps = []; + + // Check for executing tools marker + if (content.includes('[Executing tools...]')) { + steps.push({ + type: 'executing', + content: 'Executing tools...' + }); + } + + // Extract tool results with regex + const toolResultRegex = /\[Tool: ([^\]]+)\]([\s\S]*?)(?=\[|$)/g; + let match; + + while ((match = toolResultRegex.exec(content)) !== null) { + const toolName = match[1]; + const toolContent = match[2].trim(); + + steps.push({ + type: toolContent.includes('Error:') ? 'error' : 'result', + name: toolName, + content: toolContent + }); + } + + // Check for generating response marker + if (content.includes('[Generating response with tool results...]')) { + steps.push({ + type: 'generating', + content: 'Generating response with tool results...' + }); + } + + return steps; + } + + /** + * Extract the final response without tool execution steps + */ + private extractFinalResponse(content: string): string { + if (!content) return ''; + + // Remove all tool execution markers and their content + let finalResponse = content + .replace(/\[Executing tools\.\.\.\]\n*/g, '') + .replace(/\[Tool: [^\]]+\][\s\S]*?(?=\[|$)/g, '') + .replace(/\[Generating response with tool results\.\.\.\]\n*/g, ''); + + // Trim any extra whitespace + finalResponse = finalResponse.trim(); + + return finalResponse; + } + /** * Handle general errors in the send message flow */ @@ -903,40 +1200,22 @@ export default class LlmChatPanel extends BasicWidget { private showLoadingIndicator() { const logId = `ui-${Date.now()}`; - console.log(`[${logId}] Showing loading indicator and preparing tool execution display`); + console.log(`[${logId}] Showing loading indicator`); - // Ensure elements exist before trying to modify them - if (!this.loadingIndicator || !this.toolExecutionInfo || !this.toolExecutionSteps) { - console.error(`[${logId}] UI elements not properly initialized`); + // Ensure the loading indicator element exists + if (!this.loadingIndicator) { + console.error(`[${logId}] Loading indicator element not properly initialized`); return; } - // Force display of loading indicator + // Show the loading indicator try { this.loadingIndicator.style.display = 'flex'; - // Make sure tool execution info area is always visible even before we get the first event - // This helps avoid the UI getting stuck in "Processing..." state - this.toolExecutionInfo.style.display = 'block'; - - // Clear previous tool steps but add a placeholder - this.toolExecutionSteps.innerHTML = ` -
-
- - Initializing... -
-
- `; - - // Force a UI update by accessing element properties + // Force a UI update const forceUpdate = this.loadingIndicator.offsetHeight; - // Verify display states - console.log(`[${logId}] Loading indicator display state: ${this.loadingIndicator.style.display}`); - console.log(`[${logId}] Tool execution info display state: ${this.toolExecutionInfo.style.display}`); - - console.log(`[${logId}] Loading indicator and tool execution area initialized`); + console.log(`[${logId}] Loading indicator initialized`); } catch (err) { console.error(`[${logId}] Error showing loading indicator:`, err); } @@ -944,47 +1223,24 @@ export default class LlmChatPanel extends BasicWidget { private hideLoadingIndicator() { const logId = `ui-${Date.now()}`; - console.log(`[${logId}] Hiding loading indicator and tool execution area`); + console.log(`[${logId}] Hiding loading indicator`); // Ensure elements exist before trying to modify them - if (!this.loadingIndicator || !this.toolExecutionInfo) { - console.error(`[${logId}] UI elements not properly initialized`); + if (!this.loadingIndicator) { + console.error(`[${logId}] Loading indicator element not properly initialized`); return; } // Properly reset DOM elements try { - // First hide the tool execution info area - this.toolExecutionInfo.style.display = 'none'; - - // Force a UI update by accessing element properties - const forceUpdate1 = this.toolExecutionInfo.offsetHeight; - - // Then hide the loading indicator + // Hide just the loading indicator but NOT the tool execution info this.loadingIndicator.style.display = 'none'; - // Force another UI update - const forceUpdate2 = this.loadingIndicator.offsetHeight; + // Force a UI update by accessing element properties + const forceUpdate = this.loadingIndicator.offsetHeight; - // Verify display states immediately - console.log(`[${logId}] Loading indicator display state: ${this.loadingIndicator.style.display}`); - console.log(`[${logId}] Tool execution info display state: ${this.toolExecutionInfo.style.display}`); - - // Add a delay to double-check that UI updates are complete - setTimeout(() => { - console.log(`[${logId}] Verification after hide timeout: loading indicator display=${this.loadingIndicator.style.display}, tool execution info display=${this.toolExecutionInfo.style.display}`); - - // Force display none again in case something changed it - if (this.loadingIndicator.style.display !== 'none') { - console.log(`[${logId}] Loading indicator still visible after timeout, forcing hidden`); - this.loadingIndicator.style.display = 'none'; - } - - if (this.toolExecutionInfo.style.display !== 'none') { - console.log(`[${logId}] Tool execution info still visible after timeout, forcing hidden`); - this.toolExecutionInfo.style.display = 'none'; - } - }, 100); + // Tool execution info is now independent and may remain visible + console.log(`[${logId}] Loading indicator hidden, tool execution info remains visible if needed`); } catch (err) { console.error(`[${logId}] Error hiding loading indicator:`, err); } @@ -996,62 +1252,22 @@ export default class LlmChatPanel extends BasicWidget { private showToolExecutionInfo(toolExecutionData: any) { console.log(`Showing tool execution info: ${JSON.stringify(toolExecutionData)}`); - // Make sure tool execution info section is visible - this.toolExecutionInfo.style.display = 'block'; - this.loadingIndicator.style.display = 'flex'; // Ensure loading indicator is shown during tool execution + // We'll update the in-chat tool execution area in the updateStreamingUI method + // This method is now just a legacy hook for the WebSocket handlers - // Create a new step element to show the tool being executed - const stepElement = document.createElement('div'); - stepElement.className = 'tool-step my-1'; + // Make sure the loading indicator is shown during tool execution + this.loadingIndicator.style.display = 'flex'; + } - // Basic styling for the step - let stepHtml = ''; + /** + * 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 legacy hook for the WebSocket handlers - if (toolExecutionData.action === 'start') { - // Tool execution starting - stepHtml = ` -
- - ${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')} -
-
- ${this.formatToolArgs(toolExecutionData.args || {})} -
- `; - } else if (toolExecutionData.action === 'complete') { - // Tool execution completed - const resultPreview = this.formatToolResult(toolExecutionData.result); - stepHtml = ` -
- - ${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')} completed -
- ${resultPreview ? `
${resultPreview}
` : ''} - `; - } else if (toolExecutionData.action === 'error') { - // Tool execution error - stepHtml = ` -
- - ${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')} error -
-
- ${this.escapeHtml(toolExecutionData.error || 'Unknown error')} -
- `; - } - - if (stepHtml) { - stepElement.innerHTML = stepHtml; - this.toolExecutionSteps.appendChild(stepElement); - - // Scroll to bottom of tool execution steps - this.toolExecutionSteps.scrollTop = this.toolExecutionSteps.scrollHeight; - - console.log(`Added new tool execution step to UI`); - } else { - console.log(`No HTML generated for tool execution data:`, toolExecutionData); - } + // Show the loading indicator + this.loadingIndicator.style.display = 'flex'; } /** @@ -1081,49 +1297,6 @@ export default class LlmChatPanel extends BasicWidget { .join(', '); } - /** - * Format tool results for display - */ - private formatToolResult(result: any): string { - if (result === undefined || result === null) return ''; - - // Try to format as JSON if it's an object - if (typeof result === 'object') { - try { - // Get a preview of structured data - const entries = Object.entries(result); - if (entries.length === 0) return 'Empty result'; - - // Just show first 2 key-value pairs if there are many - const preview = entries.slice(0, 2).map(([key, val]) => { - let valPreview; - if (typeof val === 'string') { - valPreview = val.length > 30 ? `"${val.substring(0, 27)}..."` : `"${val}"`; - } else if (Array.isArray(val)) { - valPreview = `[${val.length} items]`; - } else if (typeof val === 'object' && val !== null) { - valPreview = '{...}'; - } else { - valPreview = String(val); - } - return `${key}: ${valPreview}`; - }).join(', '); - - return entries.length > 2 ? `${preview}, ... (${entries.length} properties)` : preview; - } catch (e) { - return String(result).substring(0, 100) + (String(result).length > 100 ? '...' : ''); - } - } - - // For string results - if (typeof result === 'string') { - return result.length > 100 ? result.substring(0, 97) + '...' : result; - } - - // Default formatting - return String(result).substring(0, 100) + (String(result).length > 100 ? '...' : ''); - } - /** * Simple HTML escaping for safer content display */ @@ -1200,26 +1373,6 @@ export default class LlmChatPanel extends BasicWidget { return processedContent; } - /** - * Show thinking state in the UI - */ - private showThinkingState(thinkingData: string) { - // Update the UI to show thinking indicator - const thinking = typeof thinkingData === 'string' ? thinkingData : 'Thinking...'; - const toolExecutionStep = document.createElement('div'); - toolExecutionStep.className = 'tool-step my-1'; - toolExecutionStep.innerHTML = ` -
- - ${this.escapeHtml(thinking)} -
- `; - - this.toolExecutionInfo.style.display = 'block'; - this.toolExecutionSteps.appendChild(toolExecutionStep); - this.toolExecutionSteps.scrollTop = this.toolExecutionSteps.scrollHeight; - } - /** * Validate embedding providers configuration * Check if there are issues with the embedding providers that might affect LLM functionality @@ -1319,4 +1472,4 @@ export default class LlmChatPanel extends BasicWidget { this.validationWarning.style.display = 'none'; } } -} \ No newline at end of file +} diff --git a/src/public/stylesheets/llm_chat.css b/src/public/stylesheets/llm_chat.css index 4b458e8b5..ae8dad6a1 100644 --- a/src/public/stylesheets/llm_chat.css +++ b/src/public/stylesheets/llm_chat.css @@ -43,6 +43,55 @@ border: 1px solid var(--subtle-border-color, var(--main-border-color)); } +/* Tool Execution Styling */ +.tool-execution-info { + margin-top: 0.75rem; + margin-bottom: 1.5rem; + border: 1px solid var(--subtle-border-color); + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); + background-color: var(--main-background-color); + /* Add a subtle transition effect */ + transition: all 0.2s ease-in-out; +} + +.tool-execution-status { + background-color: var(--accented-background-color, rgba(0, 0, 0, 0.03)) !important; + border-radius: 0 !important; + padding: 0.5rem !important; + max-height: 250px !important; + overflow-y: auto; +} + +.tool-execution-status .d-flex { + border-bottom: 1px solid var(--subtle-border-color); + padding-bottom: 0.5rem; + margin-bottom: 0.5rem; +} + +.tool-step { + padding: 0.5rem; + margin-bottom: 0.75rem; + border-radius: 0.375rem; + background-color: var(--main-background-color); + border: 1px solid var(--subtle-border-color); + transition: background-color 0.2s ease; +} + +.tool-step:hover { + background-color: rgba(0, 0, 0, 0.01); +} + +.tool-step:last-child { + margin-bottom: 0; +} + +/* Make error text more visible */ +.text-danger { + color: #dc3545 !important; +} + /* Sources Styling */ .sources-container { background-color: var(--accented-background-color, var(--main-background-color)); @@ -116,4 +165,31 @@ justify-content: center; padding: 1rem; color: var(--muted-text-color); +} + +/* Tool Execution in Chat Styling */ +.chat-tool-execution { + padding: 0 0 0 36px; /* Aligned with message content, accounting for avatar width */ + width: 100%; +} + +.tool-execution-container { + background-color: var(--accented-background-color, rgba(245, 247, 250, 0.7)); + border: 1px solid var(--subtle-border-color); + border-radius: 0.375rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + overflow: hidden; + max-width: calc(100% - 20px); +} + +.tool-execution-header { + border-bottom: 1px solid var(--subtle-border-color); + padding-bottom: 0.5rem; + color: var(--muted-text-color); +} + +.tool-execution-chat-steps { + padding: 0.5rem; + max-height: 300px; + overflow-y: auto; } \ No newline at end of file