@gitlab/duo-ui 15.0.4 → 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,11 @@
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
+
1
9
  ## [15.0.4](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.3...v15.0.4) (2025-12-03)
2
10
 
3
11
 
@@ -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',
@@ -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.4",
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>
@@ -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;