-
Notifications
You must be signed in to change notification settings - Fork 33
feat: add max-char-limit support to chat widget and web chat template #3196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9dab397
a3e5cd3
d6159f4
13a2737
823b783
af961e4
5742f3e
4f49b61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -206,10 +206,18 @@ export class OcsChat { | |
| /** | ||
| * @internal | ||
| * Optional version number of the chatbot to use. Requires authentication. | ||
| * This is for internal use only and is not intended for public-facing widgets. | ||
| * Intentionally declared as @Prop() so the Django host page can pass it as | ||
| * an HTML attribute; it is not part of the public widget API and should not | ||
| * be used by third-party embedders. | ||
| */ | ||
| @Prop() versionNumber?: number; | ||
|
|
||
| /** | ||
| * Maximum number of characters allowed in a single message (derived from the model's token limit). | ||
| * When set, a live counter is shown and the send button is disabled when exceeded. | ||
| */ | ||
| @Prop() maxCharLimit?: number; | ||
|
|
||
| @State() error: string = ''; | ||
| @State() messages: ChatMessage[] = []; | ||
| @State() sessionId?: string; | ||
|
|
@@ -495,8 +503,17 @@ export class OcsChat { | |
| } | ||
| } | ||
|
|
||
| private get messageTooLong(): boolean { | ||
| return this.maxCharLimit != null && this.messageInput.length > this.maxCharLimit; | ||
| } | ||
|
|
||
| private get messageNearLimit(): boolean { | ||
| return this.maxCharLimit != null && this.messageInput.length > this.maxCharLimit * 0.8; | ||
| } | ||
|
|
||
| private async sendMessage(message: string): Promise<void> { | ||
| if (!message.trim()) return; | ||
| if (this.messageTooLong) return; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❌ Getting worse: Complex Method |
||
| const epoch = this.sessionEpoch; | ||
|
|
||
| // Start session if we don't have one yet | ||
|
|
@@ -1748,7 +1765,7 @@ export class OcsChat { | |
| <div class="input-container"> | ||
| <textarea | ||
| ref={el => (this.textareaRef = el)} | ||
| class="message-textarea" | ||
| class={`message-textarea${this.messageTooLong ? ' message-textarea-error' : ''}`} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❌ Getting worse: Complex Method |
||
| rows={1} | ||
| placeholder={this.translationManager.get('composer.placeholder')} | ||
| value={this.messageInput} | ||
|
|
@@ -1785,13 +1802,21 @@ export class OcsChat { | |
| </button> | ||
| )} | ||
| <button | ||
| class={`send-button ${!this.isTyping && !this.isLoading && !!this.messageInput.trim() ? 'send-button-enabled' : 'send-button-disabled'}`} | ||
| class={`send-button ${ | ||
| !this.isTyping && !this.isLoading && !!this.messageInput.trim() && !this.messageTooLong ? 'send-button-enabled' : 'send-button-disabled' | ||
| }`} | ||
| onClick={() => this.sendMessage(this.messageInput)} | ||
| disabled={this.isTyping || this.isUploadingFiles || this.isLoading || !this.messageInput.trim()} | ||
| disabled={this.isTyping || this.isUploadingFiles || this.isLoading || !this.messageInput.trim() || this.messageTooLong} | ||
| title={this.messageTooLong ? this.translationManager.get('composer.messageTooLong') : undefined} | ||
| > | ||
| {this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')} | ||
| </button> | ||
| </div> | ||
| {this.maxCharLimit != null && ( | ||
| <div class={`char-counter${this.messageTooLong ? ' char-counter-error' : this.messageNearLimit ? ' char-counter-warning' : ''}`}> | ||
| {this.messageInput.length} / {this.maxCharLimit} | ||
| </div> | ||
| )} | ||
|
Comment on lines
+1815
to
+1819
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should drop this counter
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was based on changes before the #3180 feedback was implemented. |
||
| </div> | ||
| <div class="flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]"> | ||
| <p> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| user-name="{{ request.user.get_full_name }}" | ||
| {% endif %} | ||
| persistent-session="true" | ||
| {% if max_char_limit %}max-char-limit="{{ max_char_limit }}"{% endif %} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Description: Look for tests covering the new max character limit behavior.
# Expected: Matches in frontend widget tests and/or Django template/view tests.
fd -i '(test|spec)' --extension ts --extension tsx --extension js --extension jsx --extension py \
| xargs -r rg -n -C3 'maxCharLimit|max-char-limit|max_char_limit|messageTooLong|char-counter'Repository: dimagi/open-chat-studio Length of output: 51 🏁 Script executed: # First, check overall repository structure
git ls-files | grep -E '(test|spec)' | head -20Repository: dimagi/open-chat-studio Length of output: 769 🏁 Script executed: # Look for test directories
fd -t d -e '' | grep -iE '(test|spec|tests)' | head -20Repository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Check if there are any test files related to chatbot or chat functionality
fd -i 'test_.*\.py|.*_test\.py' | head -20Repository: dimagi/open-chat-studio Length of output: 809 🏁 Script executed: # Search for ChatbotMessage model and any tests that might reference it
rg -l 'ChatbotMessage' --type py | head -20Repository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Search for ChatbotMessage in the codebase to understand the model
rg 'class ChatbotMessage' -A 10 --type pyRepository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Look for any test files in chatbots app specifically
fd -i 'test.*\.py' apps/chatbotsRepository: dimagi/open-chat-studio Length of output: 193 🏁 Script executed: # Search for character limit, max char, or validation tests
rg -i 'max.*char|char.*limit|messageTooLong' --type py | grep -i testRepository: dimagi/open-chat-studio Length of output: 342 🏁 Script executed: # Check if there are frontend/widget tests
fd -t f -e spec.ts -o -e spec.tsx -o -e test.ts -o -e test.tsx | head -20Repository: dimagi/open-chat-studio Length of output: 183 🏁 Script executed: # Check the chatbot tests to see if any cover character limits
cat apps/chatbots/tests/test_chatbot_views.pyRepository: dimagi/open-chat-studio Length of output: 20332 🏁 Script executed: # Search for max_char_limit specifically in the codebase
rg 'max_char_limit|max-char-limit|maxCharLimit' --type py --type ts --type tsx --type jsRepository: dimagi/open-chat-studio Length of output: 94 🏁 Script executed: # Check the web_chat.html template to understand the context
cat templates/chatbots/chat/web_chat.htmlRepository: dimagi/open-chat-studio Length of output: 1980 🏁 Script executed: # Search for where max_char_limit is set/passed in views
rg 'max_char_limit' --type py -B 3 -A 3Repository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Check the ChatbotMessage model for any char limit fields
rg 'class ChatbotMessage' -A 50 --type pyRepository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Search for max_char_limit in template context or views related to web_chat
rg 'web_chat|max_char_limit' --type py -B 2 -A 2Repository: dimagi/open-chat-studio Length of output: 638 🏁 Script executed: # Get the full context of web_chat.html rendering in views
rg 'web_chat.html' -B 5 -A 10 --type pyRepository: dimagi/open-chat-studio Length of output: 1793 🏁 Script executed: # Search for version_specific_vars to understand what's being passed
rg 'version_specific_vars' --type py -B 3 -A 3Repository: dimagi/open-chat-studio Length of output: 7361 🏁 Script executed: # Look at the entire chatbots/views.py to find where max_char_limit might be set
head -200 apps/chatbots/views.pyRepository: dimagi/open-chat-studio Length of output: 8158 🏁 Script executed: # Check if max_char_limit is being set anywhere in the entire codebase
rg 'max_char_limit' --type pyRepository: dimagi/open-chat-studio Length of output: 49 🏁 Script executed: # Check what context variables are actually being passed to the template
grep -n 'version_specific_vars\|web_chat.html' apps/chatbots/views.py -A 5 -B 5Repository: dimagi/open-chat-studio Length of output: 2381 🏁 Script executed: # Double-check: is max_char_limit set anywhere through a middleware or context processor?
rg 'max_char_limit' --type py --type ts --type tsx --type jsRepository: dimagi/open-chat-studio Length of output: 94 🏁 Script executed: # Check context processors or template setup that might inject it
rg 'context_processor|template' apps/chatbots/views.py -B 2 -A 2Repository: dimagi/open-chat-studio Length of output: 2977 Backend context variable missing— The template references 🤖 Prompt for AI Agents |
||
| ></open-chat-studio-widget> | ||
| </div> | ||
| {% else %} | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.