@gitlab/duo-ui 15.0.3 → 15.0.5

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,18 @@
1
+ ## [15.0.5](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.4...v15.0.5) (2025-12-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Do not allow to cancel chat while tool is being processed ([41ba989](https://gitlab.com/gitlab-org/duo-ui/commit/41ba9896584762a5d0064d64a6284dd563494b46))
7
+ * Tool approval buttons enabled too fast ([8977579](https://gitlab.com/gitlab-org/duo-ui/commit/897757973ad54ac69a6bfb225df4c88bcda6426b))
8
+
9
+ ## [15.0.4](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.3...v15.0.4) (2025-12-03)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **DuoChat:** Add vertical centering for custom empty state slot ([922ed40](https://gitlab.com/gitlab-org/duo-ui/commit/922ed404f53c151ea5595e72d8905ecad42c8581))
15
+
1
16
  ## [15.0.3](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.2...v15.0.3) (2025-12-02)
2
17
 
3
18
 
@@ -305,11 +305,7 @@ var script = {
305
305
  computed: {
306
306
  canSubmit() {
307
307
  const shouldAllowSubmit = !this.isLoading && !this.isStreaming && !this.isToolApprovalProcessing && !this.isAwaitingToolApproval;
308
-
309
- // Fallback logic: If we're not loading, not streaming, and not awaiting approval,
310
- // but isToolApprovalProcessing is stuck at true, we should still enable submit
311
- const isStuckInProcessing = !this.isLoading && !this.isStreaming && !this.isAwaitingToolApproval && this.isToolApprovalProcessing;
312
- return shouldAllowSubmit || isStuckInProcessing;
308
+ return shouldAllowSubmit;
313
309
  },
314
310
  shouldShowThreadList() {
315
311
  return this.isMultithreaded && this.currentView === VIEW_TYPES.LIST;
@@ -398,6 +394,13 @@ var script = {
398
394
  return Boolean(
399
395
  // It is possible to get tool null, so we assert it is truthy to make sure there is a payload
400
396
  lastMessage && lastMessage.message_type === 'request' && lastMessage.tool_info);
397
+ },
398
+ canCancelInternal() {
399
+ // Don't allow cancel while there are pending tool approvals
400
+ if (this.isAwaitingToolApproval) {
401
+ return false;
402
+ }
403
+ return this.canCancel;
401
404
  }
402
405
  },
403
406
  watch: {
@@ -707,7 +710,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
707
710
  },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-flex gl-max-h-full gl-flex-col",class:{
708
711
  'resizable-content': _vm.shouldRenderResizable,
709
712
  'duo-chat-drawer': !_vm.shouldRenderResizable,
710
- },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,"session-id":_vm.sessionId,"agents":_vm.agents},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-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",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-mt-auto gl-px-4 gl-pb-4 gl-pt-6",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,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing,"working-directory":_vm.workingDirectory},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-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-relative gl-z-2 gl-shrink-0",attrs:{"data-testid":"chat-footer"}},[(_vm.$scopedSlots['footer-panel'])?_c('div',{staticClass:"gl-relative gl-max-w-full",attrs:{"data-testid":"footer-panel-wrapper"}},[_vm._t("footer-panel")],2):_vm._e(),_vm._v(" "),(_vm.$scopedSlots['footer-actions'])?_c('div',{staticClass:"gl-my-4 gl-flex gl-items-center gl-justify-between gl-gap-x-4 gl-px-4",attrs:{"data-testid":"footer-actions-wrapper"}},[_vm._t("footer-actions")],2):_vm._e(),_vm._v(" "),_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","disabled":_vm.isPromptEmpty || !_vm.hasValidPrompt,"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,"disabled":!_vm.canCancel},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,2657534213)},[_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",attrs:{"disabled":!_vm.canSubmit,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"character-count-limit":_vm.maxPromptLength,"textarea-classes":[
713
+ },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,"session-id":_vm.sessionId,"agents":_vm.agents},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-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",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-mt-auto gl-px-4 gl-pb-4 gl-pt-6",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,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing,"working-directory":_vm.workingDirectory},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-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-relative gl-z-2 gl-shrink-0",attrs:{"data-testid":"chat-footer"}},[(_vm.$scopedSlots['footer-panel'])?_c('div',{staticClass:"gl-relative gl-max-w-full",attrs:{"data-testid":"footer-panel-wrapper"}},[_vm._t("footer-panel")],2):_vm._e(),_vm._v(" "),(_vm.$scopedSlots['footer-actions'])?_c('div',{staticClass:"gl-my-4 gl-flex gl-items-center gl-justify-between gl-gap-x-4 gl-px-4",attrs:{"data-testid":"footer-actions-wrapper"}},[_vm._t("footer-actions")],2):_vm._e(),_vm._v(" "),_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","disabled":_vm.isPromptEmpty || !_vm.hasValidPrompt,"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,"disabled":!_vm.canCancelInternal},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,3149367298)},[_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",attrs:{"disabled":!_vm.canSubmit,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"character-count-limit":_vm.maxPromptLength,"textarea-classes":[
711
714
  'gl-absolute',
712
715
  '!gl-h-full',
713
716
  'gl-rounded-br-none',
@@ -145,6 +145,25 @@ var script = {
145
145
  required: false,
146
146
  default: ''
147
147
  },
148
+ /**
149
+ * Chat state object that contains enablement state and optional reason message.
150
+ * When chat is disabled (isEnabled: false), a reason message must be provided.
151
+ */
152
+ chatState: {
153
+ type: Object,
154
+ required: false,
155
+ default: () => ({
156
+ isEnabled: true,
157
+ reason: null
158
+ }),
159
+ validator: value => {
160
+ // If chat is disabled, reason must be provided
161
+ if (!value.isEnabled && !value.reason) {
162
+ return false;
163
+ }
164
+ return true;
165
+ }
166
+ },
148
167
  /**
149
168
  * Array of messages to display in the chat.
150
169
  */
@@ -725,7 +744,11 @@ var script = {
725
744
  const __vue_script__ = script;
726
745
 
727
746
  /* template */
728
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"markdown-code-block duo-chat web-only gl-bottom-0 gl-flex gl-max-h-full gl-flex-grow gl-flex-col",attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('web-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,"session-id":_vm.sessionId,"agents":_vm.agents,"show-studio-header":_vm.showStudioHeader},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-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",class:{ 'gl-border-t': !_vm.showStudioHeader },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-mt-auto gl-px-4 gl-pb-4 gl-pt-6",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,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing,"working-directory":_vm.workingDirectory},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-leading-20 gl-text-default gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[_c('div',{staticClass:"gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"},[_c('h1',{staticClass:"gl-my-0 gl-text-[3.5rem]",attrs:{"data-testid":"gl-duo-chat-empty-state-emoji"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_EMPTY_STATE_EMOJI)+"\n ")]),_vm._v(" "),(_vm.agentName)?_c('h2',{staticClass:"gl-heading-2 gl-my-0",attrs:{"data-testid":"gl-duo-chat-empty-state-greeting"}},[_vm._v("\n "+_vm._s(_vm.emptyStateGreeting)+"\n ")]):_vm._e(),_vm._v(" "),_c('h2',{staticClass:"gl-my-0 gl-text-size-h2",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateMainText)+"\n ")]),_vm._v(" "),_c('p',{staticClass:"gl-text-base gl-text-subtle",attrs:{"data-testid":"gl-duo-chat-empty-state-subtitle"}},[_vm._v("\n "+_vm._s(_vm.emptyStateSubText)+"\n ")])]),_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-relative gl-z-2 gl-shrink-0",attrs:{"data-testid":"chat-footer"}},[_c('p',{staticClass:"gl-mb-3 gl-text-sm gl-text-subtle",class:{ 'gl-invisible': !_vm.hasAssistantMessages },attrs:{"data-testid":"chat-disclaimer"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAIMER)+"\n ")]),_vm._v(" "),_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('div',{staticClass:"duo-chat-input gl-relative gl-min-h-8 gl-max-w-full gl-grow gl-flex-col gl-rounded-bl-[12px] gl-rounded-br-[18px] gl-rounded-tl-[12px] gl-rounded-tr-[12px] gl-align-top forced-colors:gl-border"},[(_vm.$scopedSlots['agentic-model'] || _vm.$scopedSlots['agentic-switch'])?_c('div',{staticClass:"gl-flex gl-items-center gl-justify-between gl-gap-5 gl-border-0 gl-border-b-1 gl-border-solid gl-border-strong gl-px-4 gl-py-4 forced-colors:gl-border-none"},[_c('div',{staticClass:"duo-model-switcher gl-min-w-0 gl-max-w-full"},[_vm._t("agentic-model")],2),_vm._v(" "),_c('div',{staticClass:"duo-agent-mode-switcher gl-min-w-0 gl-max-w-full gl-shrink-0"},[_vm._t("agentic-switch")],2)]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-h-[80px] gl-grow",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",attrs:{"disabled":!_vm.canSubmit,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"character-count-limit":_vm.maxPromptLength,"textarea-classes":[
747
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"markdown-code-block duo-chat web-only gl-bottom-0 gl-flex gl-max-h-full gl-flex-grow gl-flex-col",attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('web-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,"info":_vm.hasMessages ? _vm.chatState.reason : '',"is-multithreaded":_vm.isMultithreaded,"current-view":_vm.currentView,"should-render-resizable":_vm.shouldRenderResizable,"badge-type":_vm.isMultithreaded ? null : _vm.badgeType,"session-id":_vm.sessionId,"agents":_vm.agents,"show-studio-header":_vm.showStudioHeader},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-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",class:{ 'gl-border-t': !_vm.showStudioHeader },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',{class:[
748
+ 'duo-chat-history gl-px-4',
749
+ _vm.$scopedSlots['custom-empty-state']
750
+ ? 'gl-flex gl-flex-1 gl-items-center gl-justify-center'
751
+ : 'gl-mt-auto gl-pb-4 gl-pt-6' ],attrs:{"mode":"out-in","tag":"section","name":"message","data-testid":"chat-messages"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"show-delimiter":index > 0,"with-feedback":_vm.withFeedback,"is-tool-approval-processing":_vm.isToolApprovalProcessing,"working-directory":_vm.workingDirectory},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)?[_vm._t("custom-empty-state",function(){return [_c('div',{key:"empty-state-message",staticClass:"duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-default gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[_c('div',{staticClass:"gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"},[_c('h1',{staticClass:"gl-my-0 gl-text-[3.5rem]",attrs:{"data-testid":"gl-duo-chat-empty-state-emoji"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_EMPTY_STATE_EMOJI)+"\n ")]),_vm._v(" "),(_vm.agentName)?_c('h2',{staticClass:"gl-heading-2 gl-my-0",attrs:{"data-testid":"gl-duo-chat-empty-state-greeting"}},[_vm._v("\n "+_vm._s(_vm.emptyStateGreeting)+"\n ")]):_vm._e(),_vm._v(" "),_c('h2',{staticClass:"gl-my-0 gl-text-size-h2",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateMainText)+"\n ")]),_vm._v(" "),_c('p',{staticClass:"gl-text-base gl-text-subtle",attrs:{"data-testid":"gl-duo-chat-empty-state-subtitle"}},[_vm._v("\n "+_vm._s(_vm.emptyStateSubText)+"\n ")])]),_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.shouldShowThreadList)?_c('footer',{staticClass:"duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0",attrs:{"data-testid":"chat-footer"}},[_c('p',{staticClass:"gl-mb-3 gl-text-sm gl-text-subtle",class:{ 'gl-invisible': !_vm.hasAssistantMessages },attrs:{"data-testid":"chat-disclaimer"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAIMER)+"\n ")]),_vm._v(" "),_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('div',{staticClass:"duo-chat-input gl-relative gl-min-h-8 gl-max-w-full gl-grow gl-flex-col gl-rounded-bl-[12px] gl-rounded-br-[18px] gl-rounded-tl-[12px] gl-rounded-tr-[12px] gl-align-top forced-colors:gl-border"},[(_vm.$scopedSlots['agentic-model'] || _vm.$scopedSlots['agentic-switch'])?_c('div',{staticClass:"gl-flex gl-items-center gl-justify-between gl-gap-5 gl-border-0 gl-border-b-1 gl-border-solid gl-border-strong gl-px-4 gl-py-4 forced-colors:gl-border-none"},[_c('div',{staticClass:"duo-model-switcher gl-min-w-0 gl-max-w-full"},[_vm._t("agentic-model")],2),_vm._v(" "),_c('div',{staticClass:"duo-agent-mode-switcher gl-min-w-0 gl-max-w-full gl-shrink-0"},[_vm._t("agentic-switch")],2)]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-h-[80px] gl-grow",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",attrs:{"disabled":!_vm.canSubmit || !_vm.isChatAvailable || !_vm.chatState.isEnabled,"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"character-count-limit":_vm.maxPromptLength,"textarea-classes":[
729
752
  '!gl-h-full',
730
753
  '!gl-bg-transparent',
731
754
  '!gl-py-4',
@@ -747,7 +770,7 @@ return [(count <= _vm.promptLengthWarningCount)?_c('span',{staticClass:"gl-absol
747
770
  return [_c('span',{staticClass:"gl-absolute gl-bottom-[-1.6rem] gl-right-0 gl-mt-3 gl-pr-3 gl-text-sm",class:{
748
771
  'gl-bottom-[-5rem]': _vm.hasFooterControls,
749
772
  'gl-bottom-[-1.6rem]': !_vm.hasFooterControls,
750
- }},[_vm._v(_vm._s(_vm.overLimitCharacterCountMessage(count)))])]}}],null,false,4237892236),model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1),_vm._v(" "),_c('div',{staticClass:"gl-absolute gl-bottom-0 gl-right-0 gl-px-3 gl-pb-3"},[(_vm.canSubmit)?_c('gl-button',{staticClass:"!gl-rounded-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","disabled":_vm.isPromptEmpty || !_vm.hasValidPrompt,"data-testid":"chat-prompt-submit-button","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"!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}})],1)])]),_vm._v(" "),_vm._t("footer-controls")],2):_vm._e()],1)};
773
+ }},[_vm._v(_vm._s(_vm.overLimitCharacterCountMessage(count)))])]}}],null,false,4237892236),model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1),_vm._v(" "),_c('div',{staticClass:"gl-absolute gl-bottom-0 gl-right-0 gl-px-3 gl-pb-3"},[(_vm.canSubmit)?_c('gl-button',{staticClass:"!gl-rounded-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","disabled":!_vm.isChatAvailable || !_vm.chatState.isEnabled || _vm.isPromptEmpty || !_vm.hasValidPrompt,"data-testid":"chat-prompt-submit-button","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"!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}})],1)])]),_vm._v(" "),_vm._t("footer-controls")],2):_vm._e()],1)};
751
774
  var __vue_staticRenderFns__ = [];
752
775
 
753
776
  /* style */
@@ -53,6 +53,11 @@ var script = {
53
53
  required: false,
54
54
  default: ''
55
55
  },
56
+ info: {
57
+ type: String,
58
+ required: false,
59
+ default: ''
60
+ },
56
61
  isMultithreaded: {
57
62
  type: Boolean,
58
63
  required: false,
@@ -155,7 +160,7 @@ var script = {
155
160
  const __vue_script__ = script;
156
161
 
157
162
  /* template */
158
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('header',{staticClass:"gl-shrink-0 gl-p-0",attrs:{"data-testid":"chat-header"}},[(!_vm.showStudioHeader)?_c('div',{staticClass:"gl-flex gl-w-full gl-items-center gl-px-4 gl-py-3",attrs:{"data-testid":"chat-top-header"}},[(_vm.subtitle)?_c('h4',{staticClass:"gl-mb-0 gl-max-w-17/20 gl-flex-1 gl-shrink-0 gl-overflow-hidden gl-truncate gl-text-ellipsis gl-whitespace-nowrap gl-pr-3 gl-text-sm gl-font-normal gl-text-subtle",attrs:{"data-testid":"chat-subtitle"}},[_vm._v("\n "+_vm._s(_vm.subtitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-ml-auto gl-flex gl-gap-3"},[(_vm.sessionId)?_c('gl-disclosure-dropdown',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip",value:(_vm.showSessionDropdownTooltip),expression:"showSessionDropdownTooltip"}],attrs:{"icon":"ellipsis_v","category":"tertiary","text-sr-only":"","size":"small","toggle-text":_vm.$options.i18n.CHAT_DROPDOWN_MORE_OPTIONS,"items":_vm.sessionIdItems,"no-caret":""},on:{"shown":_vm.showSessionDropdown,"hidden":_vm.hideSessionDropdown}}):_vm._e(),_vm._v(" "),_c('gl-button',{attrs:{"category":"tertiary","variant":"default","icon":"close","size":"small","data-testid":"chat-close-button","aria-label":_vm.$options.i18n.CHAT_CLOSE_LABEL},on:{"click":function($event){return _vm.$emit('close')}}})],1)]):_vm._e(),_vm._v(" "),(_vm.showSubheader)?_c('div',{staticClass:"drawer-title gl-flex gl-items-center gl-justify-start gl-gap-4 gl-px-4 gl-py-3",class:{ 'gl-border-t': !_vm.showStudioHeader, 'gl-border-b': _vm.showStudioHeader },attrs:{"data-testid":"chat-subheader"}},[_c('div',{staticClass:"gl-flex gl-grow gl-gap-3"},[_c('gl-avatar',{staticClass:"gl-shrink-0",attrs:{"size":32,"entity-name":_vm.title,"shape":"circle"}}),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-flex-col gl-justify-center"},[_c('h3',{staticClass:"gl-my-0 gl-line-clamp-1 gl-text-[0.875rem] gl-text-default gl-break-anywhere"},[_vm._v("\n "+_vm._s(_vm.title)+"\n ")]),_vm._v(" "),(_vm.agentHandler)?_c('p',{staticClass:"gl-my-0 gl-text-[0.75rem] gl-text-subtle"},[_vm._v("\n "+_vm._s(_vm.agentHandler)+"\n ")]):_vm._e()])],1),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-gap-3"},[(_vm.isMultithreaded && _vm.activeThreadId && _vm.currentView === _vm.VIEW_TYPES.LIST)?_c('gl-button',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],attrs:{"title":_vm.$options.i18n.CHAT_BACK_TO_CHAT_TOOLTIP,"data-testid":"go-back-to-chat-button","category":"tertiary","size":"small","icon":"go-back","aria-label":_vm.$options.i18n.CHAT_BACK_TO_CHAT_TOOLTIP},on:{"click":function($event){return _vm.$emit('go-back-to-chat')}}}):_vm._e()],1)]):_vm._e(),_vm._v(" "),_vm._t("subheader"),_vm._v(" "),(_vm.error)?_c('gl-alert',{key:"error",staticClass:"!gl-pl-9",attrs:{"dismissible":false,"variant":"danger","role":"alert","data-testid":"chat-error"}},[_c('span',{directives:[{name:"safe-html",rawName:"v-safe-html",value:(_vm.error),expression:"error"}]})]):_vm._e()],2)};
163
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('header',{staticClass:"gl-shrink-0 gl-p-0",attrs:{"data-testid":"chat-header"}},[(!_vm.showStudioHeader)?_c('div',{staticClass:"gl-flex gl-w-full gl-items-center gl-px-4 gl-py-3",attrs:{"data-testid":"chat-top-header"}},[(_vm.subtitle)?_c('h4',{staticClass:"gl-mb-0 gl-max-w-17/20 gl-flex-1 gl-shrink-0 gl-overflow-hidden gl-truncate gl-text-ellipsis gl-whitespace-nowrap gl-pr-3 gl-text-sm gl-font-normal gl-text-subtle",attrs:{"data-testid":"chat-subtitle"}},[_vm._v("\n "+_vm._s(_vm.subtitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-ml-auto gl-flex gl-gap-3"},[(_vm.sessionId)?_c('gl-disclosure-dropdown',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip",value:(_vm.showSessionDropdownTooltip),expression:"showSessionDropdownTooltip"}],attrs:{"icon":"ellipsis_v","category":"tertiary","text-sr-only":"","size":"small","toggle-text":_vm.$options.i18n.CHAT_DROPDOWN_MORE_OPTIONS,"items":_vm.sessionIdItems,"no-caret":""},on:{"shown":_vm.showSessionDropdown,"hidden":_vm.hideSessionDropdown}}):_vm._e(),_vm._v(" "),_c('gl-button',{attrs:{"category":"tertiary","variant":"default","icon":"close","size":"small","data-testid":"chat-close-button","aria-label":_vm.$options.i18n.CHAT_CLOSE_LABEL},on:{"click":function($event){return _vm.$emit('close')}}})],1)]):_vm._e(),_vm._v(" "),(_vm.showSubheader)?_c('div',{staticClass:"drawer-title gl-flex gl-items-center gl-justify-start gl-gap-4 gl-px-4 gl-py-3",class:{ 'gl-border-t': !_vm.showStudioHeader, 'gl-border-b': _vm.showStudioHeader },attrs:{"data-testid":"chat-subheader"}},[_c('div',{staticClass:"gl-flex gl-grow gl-gap-3"},[_c('gl-avatar',{staticClass:"gl-shrink-0",attrs:{"size":32,"entity-name":_vm.title,"shape":"circle"}}),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-flex-col gl-justify-center"},[_c('h3',{staticClass:"gl-my-0 gl-line-clamp-1 gl-text-[0.875rem] gl-text-default gl-break-anywhere"},[_vm._v("\n "+_vm._s(_vm.title)+"\n ")]),_vm._v(" "),(_vm.agentHandler)?_c('p',{staticClass:"gl-my-0 gl-text-[0.75rem] gl-text-subtle"},[_vm._v("\n "+_vm._s(_vm.agentHandler)+"\n ")]):_vm._e()])],1),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-gap-3"},[(_vm.isMultithreaded && _vm.activeThreadId && _vm.currentView === _vm.VIEW_TYPES.LIST)?_c('gl-button',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],attrs:{"title":_vm.$options.i18n.CHAT_BACK_TO_CHAT_TOOLTIP,"data-testid":"go-back-to-chat-button","category":"tertiary","size":"small","icon":"go-back","aria-label":_vm.$options.i18n.CHAT_BACK_TO_CHAT_TOOLTIP},on:{"click":function($event){return _vm.$emit('go-back-to-chat')}}}):_vm._e()],1)]):_vm._e(),_vm._v(" "),_vm._t("subheader"),_vm._v(" "),(_vm.info)?_c('gl-alert',{key:"info",staticClass:"!gl-pl-9",attrs:{"dismissible":false,"variant":"info","role":"alert","data-testid":"chat-info"}},[_c('span',{directives:[{name:"safe-html",rawName:"v-safe-html",value:(_vm.info),expression:"info"}]})]):_vm._e(),_vm._v(" "),(_vm.error)?_c('gl-alert',{key:"error",staticClass:"!gl-pl-9",attrs:{"dismissible":false,"variant":"danger","role":"alert","data-testid":"chat-error"}},[_c('span',{directives:[{name:"safe-html",rawName:"v-safe-html",value:(_vm.error),expression:"error"}]})]):_vm._e()],2)};
159
164
  var __vue_staticRenderFns__ = [];
160
165
 
161
166
  /* style */
@@ -165,13 +165,14 @@ var script = {
165
165
  return this.approvalStatus === TOOL_STATUS.Approved;
166
166
  },
167
167
  isApproving() {
168
- return this.isProcessing && this.localProcessingState === PROCESSING_STATE.APPROVING;
168
+ return this.localProcessingState === PROCESSING_STATE.APPROVING || this.isProcessing;
169
169
  },
170
170
  isDenying() {
171
- return this.isProcessing && this.localProcessingState === PROCESSING_STATE.DENYING;
171
+ return this.localProcessingState === PROCESSING_STATE.DENYING;
172
172
  },
173
173
  buttonsDisabled() {
174
- return this.isProcessing;
174
+ // Disable if we've taken action locally OR if parent says we're processing
175
+ return this.localProcessingState !== PROCESSING_STATE.NONE || this.isProcessing;
175
176
  },
176
177
  approveButtonText() {
177
178
  return this.isApproving ? this.$options.i18n.APPROVING_TEXT : this.$options.i18n.APPROVE_TEXT;
@@ -197,51 +198,45 @@ var script = {
197
198
  }
198
199
  },
199
200
  watch: {
200
- // Reset local state when processing completes
201
- isProcessing(newVal, oldVal) {
202
- if (oldVal === true && newVal === false) {
203
- this.resetProcessingState();
201
+ approvalStatus(newVal) {
202
+ if (newVal === TOOL_STATUS.Approved) {
203
+ this.collapsed = true;
204
204
  }
205
205
  }
206
206
  },
207
207
  methods: {
208
- handleApprove() {
209
- if (this.isProcessing) return;
210
- this.localProcessingState = PROCESSING_STATE.APPROVING;
211
- // Default to primary option type for backward compatibility
212
- const approvalPayload = {
213
- type: this.primaryApprovalOption.type || acceptedApproveToolPayloads.APPROVE_TOOL_ONCE
214
- };
215
- this.$emit('approve-tool', approvalPayload);
216
- },
217
- handlePrimaryApprove() {
218
- if (this.isProcessing) return;
208
+ approveWithType(approvalType) {
209
+ if (this.localProcessingState !== PROCESSING_STATE.NONE) {
210
+ return;
211
+ }
219
212
  this.localProcessingState = PROCESSING_STATE.APPROVING;
220
213
  this.$emit('approve-tool', {
221
- type: this.primaryApprovalOption.type
214
+ type: approvalType
222
215
  });
223
216
  },
217
+ handleApprove() {
218
+ const approvalType = this.primaryApprovalOption.type || acceptedApproveToolPayloads.APPROVE_TOOL_ONCE;
219
+ this.approveWithType(approvalType);
220
+ },
221
+ handlePrimaryApprove() {
222
+ this.approveWithType(this.primaryApprovalOption.type);
223
+ },
224
224
  handleDropdownSelection(option) {
225
- if (this.isProcessing) return;
226
- this.localProcessingState = PROCESSING_STATE.APPROVING;
227
- this.$emit('approve-tool', {
228
- type: option.type
229
- });
225
+ this.approveWithType(option.type);
230
226
  },
231
227
  handleDeny() {
232
- if (this.isProcessing) return;
233
228
  this.showDenialReason = true;
234
229
  },
235
230
  cancelDenial() {
236
231
  this.showDenialReason = false;
237
232
  this.denialReason = '';
238
- this.localProcessingState = PROCESSING_STATE.NONE;
239
233
  },
240
234
  submitDenial() {
241
- if (this.isProcessing) return;
235
+ if (this.localProcessingState !== PROCESSING_STATE.NONE) {
236
+ return;
237
+ }
242
238
  this.localProcessingState = PROCESSING_STATE.DENYING;
243
239
  this.$emit('deny-tool', this.denialReason || null);
244
- // Don't reset state here - wait for isProcessing to change
245
240
  },
246
241
  resetProcessingState() {
247
242
  this.localProcessingState = PROCESSING_STATE.NONE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/duo-ui",
3
- "version": "15.0.3",
3
+ "version": "15.0.5",
4
4
  "description": "Duo UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -370,15 +370,7 @@ export default {
370
370
  !this.isToolApprovalProcessing &&
371
371
  !this.isAwaitingToolApproval;
372
372
 
373
- // Fallback logic: If we're not loading, not streaming, and not awaiting approval,
374
- // but isToolApprovalProcessing is stuck at true, we should still enable submit
375
- const isStuckInProcessing =
376
- !this.isLoading &&
377
- !this.isStreaming &&
378
- !this.isAwaitingToolApproval &&
379
- this.isToolApprovalProcessing;
380
-
381
- return shouldAllowSubmit || isStuckInProcessing;
373
+ return shouldAllowSubmit;
382
374
  },
383
375
  shouldShowThreadList() {
384
376
  return this.isMultithreaded && this.currentView === VIEW_TYPES.LIST;
@@ -484,6 +476,14 @@ export default {
484
476
  lastMessage && lastMessage.message_type === 'request' && lastMessage.tool_info
485
477
  );
486
478
  },
479
+ canCancelInternal() {
480
+ // Don't allow cancel while there are pending tool approvals
481
+ if (this.isAwaitingToolApproval) {
482
+ return false;
483
+ }
484
+
485
+ return this.canCancel;
486
+ },
487
487
  },
488
488
  watch: {
489
489
  multiThreadedView(newView) {
@@ -1029,7 +1029,7 @@ export default {
1029
1029
  class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full"
1030
1030
  data-testid="chat-prompt-cancel-button"
1031
1031
  :aria-label="$options.i18n.CHAT_CANCEL_LABEL"
1032
- :disabled="!canCancel"
1032
+ :disabled="!canCancelInternal"
1033
1033
  @click="cancelPrompt"
1034
1034
  />
1035
1035
  </template>
@@ -210,6 +210,22 @@ export default {
210
210
  required: false,
211
211
  default: '',
212
212
  },
213
+ /**
214
+ * Chat state object that contains enablement state and optional reason message.
215
+ * When chat is disabled (isEnabled: false), a reason message must be provided.
216
+ */
217
+ chatState: {
218
+ type: Object,
219
+ required: false,
220
+ default: () => ({ isEnabled: true, reason: null }),
221
+ validator: (value) => {
222
+ // If chat is disabled, reason must be provided
223
+ if (!value.isEnabled && !value.reason) {
224
+ return false;
225
+ }
226
+ return true;
227
+ },
228
+ },
213
229
  /**
214
230
  * Array of messages to display in the chat.
215
231
  */
@@ -824,6 +840,7 @@ export default {
824
840
  :title="isMultithreaded && currentView === 'list' ? $options.i18n.CHAT_HISTORY_TITLE : title"
825
841
  :subtitle="activeThreadTitleForView"
826
842
  :error="error"
843
+ :info="hasMessages ? chatState.reason : ''"
827
844
  :is-multithreaded="isMultithreaded"
828
845
  :current-view="currentView"
829
846
  :should-render-resizable="shouldRenderResizable"
@@ -860,7 +877,13 @@ export default {
860
877
  mode="out-in"
861
878
  tag="section"
862
879
  name="message"
863
- class="duo-chat-history gl-mt-auto gl-px-4 gl-pb-4 gl-pt-6"
880
+ data-testid="chat-messages"
881
+ :class="[
882
+ 'duo-chat-history gl-px-4',
883
+ $scopedSlots['custom-empty-state']
884
+ ? 'gl-flex gl-flex-1 gl-items-center gl-justify-center'
885
+ : 'gl-mt-auto gl-pb-4 gl-pt-6',
886
+ ]"
864
887
  >
865
888
  <duo-chat-conversation
866
889
  v-for="(conversation, index) in conversations"
@@ -881,44 +904,49 @@ export default {
881
904
  @open-file-path="onOpenFilePath"
882
905
  />
883
906
  <template v-if="!hasMessages && !isLoading">
884
- <div
885
- key="empty-state-message"
886
- class="duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-default gl-break-anywhere"
887
- data-testid="gl-duo-chat-empty-state"
888
- >
907
+ <slot name="custom-empty-state">
889
908
  <div
890
- class="gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"
909
+ key="empty-state-message"
910
+ class="duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-default gl-break-anywhere"
911
+ data-testid="gl-duo-chat-empty-state"
891
912
  >
892
- <h1 class="gl-my-0 gl-text-[3.5rem]" data-testid="gl-duo-chat-empty-state-emoji">
893
- {{ $options.i18n.CHAT_EMPTY_STATE_EMOJI }}
894
- </h1>
895
- <h2
896
- v-if="agentName"
897
- class="gl-heading-2 gl-my-0"
898
- data-testid="gl-duo-chat-empty-state-greeting"
913
+ <div
914
+ class="gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"
899
915
  >
900
- {{ emptyStateGreeting }}
901
- </h2>
902
- <h2 class="gl-my-0 gl-text-size-h2" data-testid="gl-duo-chat-empty-state-title">
903
- {{ emptyStateMainText }}
904
- </h2>
905
- <p class="gl-text-base gl-text-subtle" data-testid="gl-duo-chat-empty-state-subtitle">
906
- {{ emptyStateSubText }}
907
- </p>
916
+ <h1 class="gl-my-0 gl-text-[3.5rem]" data-testid="gl-duo-chat-empty-state-emoji">
917
+ {{ $options.i18n.CHAT_EMPTY_STATE_EMOJI }}
918
+ </h1>
919
+ <h2
920
+ v-if="agentName"
921
+ class="gl-heading-2 gl-my-0"
922
+ data-testid="gl-duo-chat-empty-state-greeting"
923
+ >
924
+ {{ emptyStateGreeting }}
925
+ </h2>
926
+ <h2 class="gl-my-0 gl-text-size-h2" data-testid="gl-duo-chat-empty-state-title">
927
+ {{ emptyStateMainText }}
928
+ </h2>
929
+ <p
930
+ class="gl-text-base gl-text-subtle"
931
+ data-testid="gl-duo-chat-empty-state-subtitle"
932
+ >
933
+ {{ emptyStateSubText }}
934
+ </p>
935
+ </div>
936
+ <duo-chat-predefined-prompts
937
+ key="predefined-prompts"
938
+ :prompts="predefinedPrompts"
939
+ @click="sendPredefinedPrompt"
940
+ />
908
941
  </div>
909
- <duo-chat-predefined-prompts
910
- key="predefined-prompts"
911
- :prompts="predefinedPrompts"
912
- @click="sendPredefinedPrompt"
913
- />
914
- </div>
942
+ </slot>
915
943
  </template>
916
944
  <duo-chat-loader v-if="isLoading" key="loader" :tool-name="toolName" />
917
945
  <div key="anchor" ref="anchor" class="scroll-anchor"></div>
918
946
  </transition-group>
919
947
  </div>
920
948
  <footer
921
- v-if="isChatAvailable && !shouldShowThreadList"
949
+ v-if="!shouldShowThreadList"
922
950
  data-testid="chat-footer"
923
951
  class="duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0"
924
952
  >
@@ -983,7 +1011,7 @@ export default {
983
1011
  <gl-form-textarea
984
1012
  ref="prompt"
985
1013
  v-model="prompt"
986
- :disabled="!canSubmit"
1014
+ :disabled="!canSubmit || !isChatAvailable || !chatState.isEnabled"
987
1015
  data-testid="chat-prompt-input"
988
1016
  :placeholder="inputPlaceholder"
989
1017
  :character-count-limit="maxPromptLength"
@@ -1045,7 +1073,9 @@ export default {
1045
1073
  variant="confirm"
1046
1074
  class="!gl-rounded-full"
1047
1075
  type="submit"
1048
- :disabled="isPromptEmpty || !hasValidPrompt"
1076
+ :disabled="
1077
+ !isChatAvailable || !chatState.isEnabled || isPromptEmpty || !hasValidPrompt
1078
+ "
1049
1079
  data-testid="chat-prompt-submit-button"
1050
1080
  :aria-label="$options.i18n.CHAT_SUBMIT_LABEL"
1051
1081
  />
@@ -70,6 +70,11 @@ export default {
70
70
  required: false,
71
71
  default: '',
72
72
  },
73
+ info: {
74
+ type: String,
75
+ required: false,
76
+ default: '',
77
+ },
73
78
  isMultithreaded: {
74
79
  type: Boolean,
75
80
  required: false,
@@ -244,6 +249,18 @@ export default {
244
249
 
245
250
  <slot name="subheader"></slot>
246
251
 
252
+ <gl-alert
253
+ v-if="info"
254
+ key="info"
255
+ :dismissible="false"
256
+ variant="info"
257
+ class="!gl-pl-9"
258
+ role="alert"
259
+ data-testid="chat-info"
260
+ >
261
+ <span v-safe-html="info"></span>
262
+ </gl-alert>
263
+
247
264
  <gl-alert
248
265
  v-if="error"
249
266
  key="error"
@@ -189,13 +189,14 @@ export default {
189
189
  return this.approvalStatus === TOOL_STATUS.Approved;
190
190
  },
191
191
  isApproving() {
192
- return this.isProcessing && this.localProcessingState === PROCESSING_STATE.APPROVING;
192
+ return this.localProcessingState === PROCESSING_STATE.APPROVING || this.isProcessing;
193
193
  },
194
194
  isDenying() {
195
- return this.isProcessing && this.localProcessingState === PROCESSING_STATE.DENYING;
195
+ return this.localProcessingState === PROCESSING_STATE.DENYING;
196
196
  },
197
197
  buttonsDisabled() {
198
- return this.isProcessing;
198
+ // Disable if we've taken action locally OR if parent says we're processing
199
+ return this.localProcessingState !== PROCESSING_STATE.NONE || this.isProcessing;
199
200
  },
200
201
  approveButtonText() {
201
202
  return this.isApproving ? this.$options.i18n.APPROVING_TEXT : this.$options.i18n.APPROVE_TEXT;
@@ -221,52 +222,46 @@ export default {
221
222
  },
222
223
  },
223
224
  watch: {
224
- // Reset local state when processing completes
225
- isProcessing(newVal, oldVal) {
226
- if (oldVal === true && newVal === false) {
227
- this.resetProcessingState();
225
+ approvalStatus(newVal) {
226
+ if (newVal === TOOL_STATUS.Approved) {
227
+ this.collapsed = true;
228
228
  }
229
229
  },
230
230
  },
231
231
  methods: {
232
- handleApprove() {
233
- if (this.isProcessing) return;
232
+ approveWithType(approvalType) {
233
+ if (this.localProcessingState !== PROCESSING_STATE.NONE) {
234
+ return;
235
+ }
234
236
 
235
237
  this.localProcessingState = PROCESSING_STATE.APPROVING;
236
- // Default to primary option type for backward compatibility
237
- const approvalPayload = {
238
- type: this.primaryApprovalOption.type || acceptedApproveToolPayloads.APPROVE_TOOL_ONCE,
239
- };
240
- this.$emit('approve-tool', approvalPayload);
238
+ this.$emit('approve-tool', { type: approvalType });
239
+ },
240
+ handleApprove() {
241
+ const approvalType =
242
+ this.primaryApprovalOption.type || acceptedApproveToolPayloads.APPROVE_TOOL_ONCE;
243
+ this.approveWithType(approvalType);
241
244
  },
242
245
  handlePrimaryApprove() {
243
- if (this.isProcessing) return;
244
-
245
- this.localProcessingState = PROCESSING_STATE.APPROVING;
246
- this.$emit('approve-tool', { type: this.primaryApprovalOption.type });
246
+ this.approveWithType(this.primaryApprovalOption.type);
247
247
  },
248
248
  handleDropdownSelection(option) {
249
- if (this.isProcessing) return;
250
-
251
- this.localProcessingState = PROCESSING_STATE.APPROVING;
252
- this.$emit('approve-tool', { type: option.type });
249
+ this.approveWithType(option.type);
253
250
  },
254
251
  handleDeny() {
255
- if (this.isProcessing) return;
256
-
257
252
  this.showDenialReason = true;
258
253
  },
259
254
  cancelDenial() {
260
255
  this.showDenialReason = false;
261
256
  this.denialReason = '';
262
- this.localProcessingState = PROCESSING_STATE.NONE;
263
257
  },
264
258
  submitDenial() {
265
- if (this.isProcessing) return;
259
+ if (this.localProcessingState !== PROCESSING_STATE.NONE) {
260
+ return;
261
+ }
266
262
 
267
263
  this.localProcessingState = PROCESSING_STATE.DENYING;
268
264
  this.$emit('deny-tool', this.denialReason || null);
269
- // Don't reset state here - wait for isProcessing to change
270
265
  },
271
266
  resetProcessingState() {
272
267
  this.localProcessingState = PROCESSING_STATE.NONE;