@gitlab/duo-ui 9.1.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # [10.0.0](https://gitlab.com/gitlab-org/duo-ui/compare/v9.1.1...v10.0.0) (2025-07-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * agentic chat resolve input not clearing after submission ([68c9dee](https://gitlab.com/gitlab-org/duo-ui/commit/68c9deeda19073fdce046544b4adc3f826189ec0))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * The chat input would remain populated after sending a
12
+ message due to a race condition where the textarea was disabled before
13
+ the prompt could be cleared. This happened because `canSubmit = [secure]`
14
+ executed before `setPromptAndFocus()` completed its async DOM update.
15
+
16
+ ## [9.1.1](https://gitlab.com/gitlab-org/duo-ui/compare/v9.1.0...v9.1.1) (2025-07-02)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * propagate the open-file event for agentic chat ([72d21a7](https://gitlab.com/gitlab-org/duo-ui/commit/72d21a7f1d5ca33db9da4599c28455f9ea95f5f3))
22
+
1
23
  # [9.1.0](https://gitlab.com/gitlab-org/duo-ui/compare/v9.0.1...v9.1.0) (2025-07-01)
2
24
 
3
25
 
@@ -2,7 +2,7 @@ import throttle from 'lodash/throttle';
2
2
  import VueResizable from 'vue-resizable';
3
3
  import { GlButton, GlAlert, GlFormInputGroup, GlFormTextarea, GlForm, GlExperimentBadge, GlCard, GlDropdownItem, GlSafeHtmlDirective } from '@gitlab/ui';
4
4
  import { translate } from '../../utils/i18n';
5
- import { badgeTypes, badgeTypeValidator, CHAT_RESET_MESSAGE, CHAT_INCLUDE_MESSAGE, MESSAGE_MODEL_ROLES, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE } from '../chat/constants';
5
+ import { badgeTypes, badgeTypeValidator, CHAT_RESET_MESSAGE, CHAT_INCLUDE_MESSAGE, MESSAGE_MODEL_ROLES, CHAT_BASE_COMMANDS } from '../chat/constants';
6
6
  import { VIEW_TYPES } from '../chat/components/duo_chat_header/constants';
7
7
  import DuoChatLoader from '../chat/components/duo_chat_loader/duo_chat_loader';
8
8
  import DuoChatPredefinedPrompts from '../chat/components/duo_chat_predefined_prompts/duo_chat_predefined_prompts';
@@ -427,26 +427,35 @@ var script = {
427
427
  this.$emit('chat-cancel');
428
428
  this.setPromptAndFocus();
429
429
  },
430
- sendChatPrompt() {
430
+ async sendChatPrompt() {
431
431
  if (!this.canSubmit || this.contextItemsMenuIsOpen) {
432
432
  return;
433
433
  }
434
434
  if (this.prompt) {
435
- if (this.caseInsensitivePrompt.startsWith(CHAT_INCLUDE_MESSAGE) && this.hasContextItemSelectionMenu) {
435
+ // Store these before any async operations that might clear the prompt
436
+ const trimmedPrompt = this.prompt.trim();
437
+ const lowerCasePrompt = this.prompt.toLowerCase().trim();
438
+ if (lowerCasePrompt.startsWith(CHAT_INCLUDE_MESSAGE) && this.hasContextItemSelectionMenu) {
436
439
  this.contextItemsMenuIsOpen = true;
437
440
  return;
438
441
  }
439
- if (![CHAT_RESET_MESSAGE, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE].includes(this.caseInsensitivePrompt)) {
440
- this.canSubmit = false;
441
- }
442
442
 
443
443
  /**
444
444
  * Emitted when a new user prompt should be sent out.
445
445
  *
446
446
  * @param {String} prompt The user prompt to send.
447
447
  */
448
- this.$emit('send-chat-prompt', this.prompt.trim());
449
- this.setPromptAndFocus();
448
+ this.$emit('send-chat-prompt', trimmedPrompt);
449
+
450
+ // Always clear the prompt after sending, regardless of the command type
451
+ await this.setPromptAndFocus();
452
+
453
+ // Check if it was a special command using the stored value (before clearing)
454
+ if (!CHAT_BASE_COMMANDS.includes(lowerCasePrompt)) {
455
+ // Wait for all reactive updates to complete before setting canSubmit
456
+ await this.$nextTick();
457
+ this.canSubmit = false;
458
+ }
450
459
  }
451
460
  },
452
461
  sendPredefinedPrompt(prompt) {
@@ -614,10 +623,24 @@ var script = {
614
623
  this.$emit('delete-thread', threadId);
615
624
  },
616
625
  onApproveToolCall() {
626
+ /**
627
+ * Emitted when a user approves a tool call.
628
+ */
617
629
  this.$emit('approve-tool');
618
630
  },
619
631
  onDenyToolCall(reason) {
632
+ /**
633
+ * Emitted when a user denies a tool call.
634
+ * @param {String} reason The reason for denying the tool call.
635
+ */
620
636
  this.$emit('deny-tool', reason);
637
+ },
638
+ onOpenFilePath(filePath) {
639
+ /**
640
+ * Emitted when a file path link is clicked in a chat message.
641
+ * @param {String} filePath The file path to open
642
+ */
643
+ this.$emit('open-file-path', filePath);
621
644
  }
622
645
  },
623
646
  i18n
@@ -633,7 +656,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
633
656
  },attrs:{"width":_vm.shouldRenderResizable ? _vm.dimensions.width : null,"height":_vm.shouldRenderResizable ? _vm.dimensions.height : null,"max-width":_vm.shouldRenderResizable ? _vm.dimensions.maxWidth : null,"max-height":_vm.shouldRenderResizable ? _vm.dimensions.maxHeight : null,"min-width":_vm.shouldRenderResizable ? _vm.dimensions.minWidth : null,"left":_vm.shouldRenderResizable ? _vm.dimensions.left : null,"top":_vm.shouldRenderResizable ? _vm.dimensions.top : null,"fit-parent":true,"min-height":_vm.shouldRenderResizable ? _vm.dimensions.minHeight : null,"active":_vm.shouldRenderResizable ? ['l', 't', 'lt'] : null},on:{"resize:end":_vm.updateSize}},[(!_vm.isHidden)?_c('aside',{staticClass:"markdown-code-block duo-chat gl-bottom-0 gl-max-h-full gl-flex gl-flex-col",class:{
634
657
  'resizable-content': _vm.shouldRenderResizable,
635
658
  'duo-chat-drawer': !_vm.shouldRenderResizable,
636
- },attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('duo-chat-header',{ref:"header",attrs:{"active-thread-id":_vm.activeThreadId,"title":_vm.isMultithreaded && _vm.currentView === 'list' ? _vm.$options.i18n.CHAT_HISTORY_TITLE : _vm.title,"subtitle":_vm.activeThreadTitleForView,"error":_vm.error,"is-multithreaded":_vm.isMultithreaded,"current-view":_vm.currentView,"should-render-resizable":_vm.shouldRenderResizable,"badge-type":_vm.isMultithreaded ? null : _vm.badgeType},on:{"go-back":_vm.onGoBack,"new-chat":_vm.onNewChat,"close":_vm.hideChat},scopedSlots:_vm._u([{key:"subheader",fn:function(){return [_vm._t("subheader")]},proxy:true}],null,true)}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-overflow-y-auto gl-flex gl-flex-col gl-flex-1 gl-flex-grow gl-bg-inherit gl-overscroll-contain",attrs:{"data-testid":"chat-history"},on:{"scroll":_vm.handleScrollingThrottled}},[(_vm.shouldShowThreadList)?_c('duo-chat-threads',{attrs:{"threads":_vm.threadList,"preferred-locale":_vm.preferredLocale},on:{"new-chat":_vm.onNewChat,"select-thread":_vm.onSelectThread,"delete-thread":_vm.onDeleteThread,"close":_vm.hideChat}}):_c('transition-group',{staticClass:"duo-chat-history gl-p-5 gl-mt-auto",attrs:{"mode":"out-in","tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"copy-code-snippet":_vm.onCopyCodeSnippet,"copy-message":_vm.onCopyMessage,"get-context-item-content":_vm.onGetContextItemContent,"approve-tool":_vm.onApproveToolCall,"deny-tool":_vm.onDenyToolCall}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state-message",staticClass:"duo-chat-message gl-rounded-bl-none gl-border-1 gl-border-solid gl-border-gray-50 gl-bg-gray-10 gl-p-4 gl-leading-20 gl-text-gray-900 gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[(_vm.emptyStateTitle)?_c('p',{staticClass:"gl-m-0",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateTitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})],1)]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('duo-chat-loader',{key:"loader",attrs:{"tool-name":_vm.toolName}}):_vm._e(),_vm._v(" "),_c('div',{key:"anchor",ref:"anchor",staticClass:"scroll-anchor"})],2)],1),_vm._v(" "),(_vm.isChatAvailable && !_vm.shouldShowThreadList)?_c('footer',{staticClass:"duo-chat-drawer-footer gl-border-0 gl-bg-default gl-pb-3 gl-shrink-0 gl-relative gl-z-2",class:{ 'duo-chat-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_c('gl-form',{attrs:{"data-testid":"chat-prompt-form"},on:{"submit":function($event){$event.stopPropagation();$event.preventDefault();return _vm.sendChatPrompt.apply(null, arguments)}}},[_c('div',{staticClass:"gl-relative gl-max-w-full"},[_vm._t("context-items-menu",null,{"isOpen":_vm.contextItemsMenuIsOpen,"onClose":_vm.closeContextItemsMenuOpen,"setRef":_vm.setContextItemsMenuRef,"focusPrompt":_vm.focusChatInput})],2),_vm._v(" "),_c('gl-form-input-group',{scopedSlots:_vm._u([{key:"append",fn:function(){return [(_vm.canSubmit)?_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","data-testid":"chat-prompt-submit-button","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full",attrs:{"icon":"stop","category":"primary","variant":"default","data-testid":"chat-prompt-cancel-button","aria-label":_vm.$options.i18n.CHAT_CANCEL_LABEL},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,3738248012)},[_c('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-align-top",attrs:{"data-value":_vm.prompt}},[(_vm.shouldShowSlashCommands)?_c('gl-card',{ref:"commands",staticClass:"slash-commands !gl-absolute gl-w-full -gl-translate-y-full gl-list-none gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2"}},_vm._l((_vm.filteredSlashCommands),function(command,index){return _c('gl-dropdown-item',{key:command.name,class:{ 'active-command': index === _vm.activeCommandIndex },on:{"click":function($event){return _vm.selectSlashCommand(index)}},nativeOn:{"mouseenter":function($event){_vm.activeCommandIndex = index;}}},[_c('span',{staticClass:"gl-flex gl-justify-between"},[_c('span',{staticClass:"gl-block"},[_vm._v(_vm._s(command.name))]),_vm._v(" "),_c('small',{staticClass:"gl-pl-3 gl-text-right gl-italic gl-text-subtle"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute !gl-h-full gl-rounded-br-none gl-rounded-tr-none !gl-bg-transparent !gl-py-4 !gl-shadow-none",class:{ 'gl-truncate': !_vm.prompt },attrs:{"disabled":!_vm.canSubmit,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"autofocus":""},on:{"compositionend":_vm.compositionEnd},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)])],1),_vm._v(" "),_c('p',{staticClass:"gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAMER)+"\n ")])],1):_vm._e()],1):_vm._e()])};
659
+ },attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('duo-chat-header',{ref:"header",attrs:{"active-thread-id":_vm.activeThreadId,"title":_vm.isMultithreaded && _vm.currentView === 'list' ? _vm.$options.i18n.CHAT_HISTORY_TITLE : _vm.title,"subtitle":_vm.activeThreadTitleForView,"error":_vm.error,"is-multithreaded":_vm.isMultithreaded,"current-view":_vm.currentView,"should-render-resizable":_vm.shouldRenderResizable,"badge-type":_vm.isMultithreaded ? null : _vm.badgeType},on:{"go-back":_vm.onGoBack,"new-chat":_vm.onNewChat,"close":_vm.hideChat},scopedSlots:_vm._u([{key:"subheader",fn:function(){return [_vm._t("subheader")]},proxy:true}],null,true)}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-overflow-y-auto gl-flex gl-flex-col gl-flex-1 gl-flex-grow gl-bg-inherit gl-overscroll-contain",attrs:{"data-testid":"chat-history"},on:{"scroll":_vm.handleScrollingThrottled}},[(_vm.shouldShowThreadList)?_c('duo-chat-threads',{attrs:{"threads":_vm.threadList,"preferred-locale":_vm.preferredLocale},on:{"new-chat":_vm.onNewChat,"select-thread":_vm.onSelectThread,"delete-thread":_vm.onDeleteThread,"close":_vm.hideChat}}):_c('transition-group',{staticClass:"duo-chat-history gl-p-5 gl-mt-auto",attrs:{"mode":"out-in","tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"copy-code-snippet":_vm.onCopyCodeSnippet,"copy-message":_vm.onCopyMessage,"get-context-item-content":_vm.onGetContextItemContent,"approve-tool":_vm.onApproveToolCall,"deny-tool":_vm.onDenyToolCall,"open-file-path":_vm.onOpenFilePath}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state-message",staticClass:"duo-chat-message gl-rounded-bl-none gl-border-1 gl-border-solid gl-border-gray-50 gl-bg-gray-10 gl-p-4 gl-leading-20 gl-text-gray-900 gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[(_vm.emptyStateTitle)?_c('p',{staticClass:"gl-m-0",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateTitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})],1)]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('duo-chat-loader',{key:"loader",attrs:{"tool-name":_vm.toolName}}):_vm._e(),_vm._v(" "),_c('div',{key:"anchor",ref:"anchor",staticClass:"scroll-anchor"})],2)],1),_vm._v(" "),(_vm.isChatAvailable && !_vm.shouldShowThreadList)?_c('footer',{staticClass:"duo-chat-drawer-footer gl-border-0 gl-bg-default gl-pb-3 gl-shrink-0 gl-relative gl-z-2",class:{ 'duo-chat-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_c('gl-form',{attrs:{"data-testid":"chat-prompt-form"},on:{"submit":function($event){$event.stopPropagation();$event.preventDefault();return _vm.sendChatPrompt.apply(null, arguments)}}},[_c('div',{staticClass:"gl-relative gl-max-w-full"},[_vm._t("context-items-menu",null,{"isOpen":_vm.contextItemsMenuIsOpen,"onClose":_vm.closeContextItemsMenuOpen,"setRef":_vm.setContextItemsMenuRef,"focusPrompt":_vm.focusChatInput})],2),_vm._v(" "),_c('gl-form-input-group',{scopedSlots:_vm._u([{key:"append",fn:function(){return [(_vm.canSubmit)?_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","data-testid":"chat-prompt-submit-button","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full",attrs:{"icon":"stop","category":"primary","variant":"default","data-testid":"chat-prompt-cancel-button","aria-label":_vm.$options.i18n.CHAT_CANCEL_LABEL},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,3738248012)},[_c('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-align-top",attrs:{"data-value":_vm.prompt}},[(_vm.shouldShowSlashCommands)?_c('gl-card',{ref:"commands",staticClass:"slash-commands !gl-absolute gl-w-full -gl-translate-y-full gl-list-none gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2"}},_vm._l((_vm.filteredSlashCommands),function(command,index){return _c('gl-dropdown-item',{key:command.name,class:{ 'active-command': index === _vm.activeCommandIndex },on:{"click":function($event){return _vm.selectSlashCommand(index)}},nativeOn:{"mouseenter":function($event){_vm.activeCommandIndex = index;}}},[_c('span',{staticClass:"gl-flex gl-justify-between"},[_c('span',{staticClass:"gl-block"},[_vm._v(_vm._s(command.name))]),_vm._v(" "),_c('small',{staticClass:"gl-pl-3 gl-text-right gl-italic gl-text-subtle"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute !gl-h-full gl-rounded-br-none gl-rounded-tr-none !gl-bg-transparent !gl-py-4 !gl-shadow-none",class:{ 'gl-truncate': !_vm.prompt },attrs:{"disabled":!_vm.canSubmit,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"autofocus":""},on:{"compositionend":_vm.compositionEnd},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)])],1),_vm._v(" "),_c('p',{staticClass:"gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAMER)+"\n ")])],1):_vm._e()],1):_vm._e()])};
637
660
  var __vue_staticRenderFns__ = [];
638
661
 
639
662
  /* style */
@@ -2,6 +2,7 @@ const CHAT_RESET_MESSAGE = '/reset';
2
2
  const CHAT_CLEAR_MESSAGE = '/clear';
3
3
  const CHAT_NEW_MESSAGE = '/new';
4
4
  const CHAT_INCLUDE_MESSAGE = '/include';
5
+ const CHAT_BASE_COMMANDS = [CHAT_RESET_MESSAGE, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE];
5
6
  const LOADING_TRANSITION_DURATION = 7500;
6
7
  const DOCUMENTATION_SOURCE_TYPES = {
7
8
  HANDBOOK: {
@@ -29,4 +30,4 @@ const SELECTED_CONTEXT_ITEMS_DEFAULT_COLLAPSED = true;
29
30
  const badgeTypes = ['experiment', 'beta', null];
30
31
  const badgeTypeValidator = value => badgeTypes.includes(value);
31
32
 
32
- export { CHAT_CLEAR_MESSAGE, CHAT_INCLUDE_MESSAGE, CHAT_NEW_MESSAGE, CHAT_RESET_MESSAGE, DOCUMENTATION_SOURCE_TYPES, LOADING_TRANSITION_DURATION, MESSAGE_MODEL_ROLES, SELECTED_CONTEXT_ITEMS_DEFAULT_COLLAPSED, badgeTypeValidator, badgeTypes };
33
+ export { CHAT_BASE_COMMANDS, CHAT_CLEAR_MESSAGE, CHAT_INCLUDE_MESSAGE, CHAT_NEW_MESSAGE, CHAT_RESET_MESSAGE, DOCUMENTATION_SOURCE_TYPES, LOADING_TRANSITION_DURATION, MESSAGE_MODEL_ROLES, SELECTED_CONTEXT_ITEMS_DEFAULT_COLLAPSED, badgeTypeValidator, badgeTypes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/duo-ui",
3
- "version": "9.1.0",
3
+ "version": "10.0.0",
4
4
  "description": "Duo UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,9 +20,8 @@ import {
20
20
  badgeTypes,
21
21
  badgeTypeValidator,
22
22
  CHAT_RESET_MESSAGE,
23
- CHAT_CLEAR_MESSAGE,
24
- CHAT_NEW_MESSAGE,
25
23
  CHAT_INCLUDE_MESSAGE,
24
+ CHAT_BASE_COMMANDS,
26
25
  MESSAGE_MODEL_ROLES,
27
26
  } from '../chat/constants';
28
27
  import { VIEW_TYPES } from '../chat/components/duo_chat_header/constants';
@@ -501,35 +500,36 @@ export default {
501
500
  this.$emit('chat-cancel');
502
501
  this.setPromptAndFocus();
503
502
  },
504
- sendChatPrompt() {
503
+ async sendChatPrompt() {
505
504
  if (!this.canSubmit || this.contextItemsMenuIsOpen) {
506
505
  return;
507
506
  }
508
507
  if (this.prompt) {
509
- if (
510
- this.caseInsensitivePrompt.startsWith(CHAT_INCLUDE_MESSAGE) &&
511
- this.hasContextItemSelectionMenu
512
- ) {
508
+ // Store these before any async operations that might clear the prompt
509
+ const trimmedPrompt = this.prompt.trim();
510
+ const lowerCasePrompt = this.prompt.toLowerCase().trim();
511
+
512
+ if (lowerCasePrompt.startsWith(CHAT_INCLUDE_MESSAGE) && this.hasContextItemSelectionMenu) {
513
513
  this.contextItemsMenuIsOpen = true;
514
514
  return;
515
515
  }
516
516
 
517
- if (
518
- ![CHAT_RESET_MESSAGE, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE].includes(
519
- this.caseInsensitivePrompt
520
- )
521
- ) {
522
- this.canSubmit = false;
523
- }
524
-
525
517
  /**
526
518
  * Emitted when a new user prompt should be sent out.
527
519
  *
528
520
  * @param {String} prompt The user prompt to send.
529
521
  */
530
- this.$emit('send-chat-prompt', this.prompt.trim());
522
+ this.$emit('send-chat-prompt', trimmedPrompt);
531
523
 
532
- this.setPromptAndFocus();
524
+ // Always clear the prompt after sending, regardless of the command type
525
+ await this.setPromptAndFocus();
526
+
527
+ // Check if it was a special command using the stored value (before clearing)
528
+ if (!CHAT_BASE_COMMANDS.includes(lowerCasePrompt)) {
529
+ // Wait for all reactive updates to complete before setting canSubmit
530
+ await this.$nextTick();
531
+ this.canSubmit = false;
532
+ }
533
533
  }
534
534
  },
535
535
  sendPredefinedPrompt(prompt) {
@@ -689,11 +689,25 @@ export default {
689
689
  this.$emit('delete-thread', threadId);
690
690
  },
691
691
  onApproveToolCall() {
692
+ /**
693
+ * Emitted when a user approves a tool call.
694
+ */
692
695
  this.$emit('approve-tool');
693
696
  },
694
697
  onDenyToolCall(reason) {
698
+ /**
699
+ * Emitted when a user denies a tool call.
700
+ * @param {String} reason The reason for denying the tool call.
701
+ */
695
702
  this.$emit('deny-tool', reason);
696
703
  },
704
+ onOpenFilePath(filePath) {
705
+ /**
706
+ * Emitted when a file path link is clicked in a chat message.
707
+ * @param {String} filePath The file path to open
708
+ */
709
+ this.$emit('open-file-path', filePath);
710
+ },
697
711
  },
698
712
  i18n,
699
713
  };
@@ -787,6 +801,7 @@ export default {
787
801
  @get-context-item-content="onGetContextItemContent"
788
802
  @approve-tool="onApproveToolCall"
789
803
  @deny-tool="onDenyToolCall"
804
+ @open-file-path="onOpenFilePath"
790
805
  />
791
806
  <template v-if="!hasMessages && !isLoading">
792
807
  <div
@@ -3,6 +3,8 @@ export const CHAT_CLEAR_MESSAGE = '/clear';
3
3
  export const CHAT_NEW_MESSAGE = '/new';
4
4
  export const CHAT_INCLUDE_MESSAGE = '/include';
5
5
 
6
+ export const CHAT_BASE_COMMANDS = [CHAT_RESET_MESSAGE, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE];
7
+
6
8
  export const LOADING_TRANSITION_DURATION = 7500;
7
9
 
8
10
  export const DOCUMENTATION_SOURCE_TYPES = {