@gitlab/duo-ui 15.0.5 → 15.0.7

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.7](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.6...v15.0.7) (2025-12-08)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **DuoChat:** Apply centering classes conditionally for custom-empty-state slot ([4dc6b50](https://gitlab.com/gitlab-org/duo-ui/commit/4dc6b50224e3d53eb5303d8a3946e2faa0a26ef5))
7
+
8
+ ## [15.0.6](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.5...v15.0.6) (2025-12-08)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * trustedUrls propagation to renderMarkdown ([7e296d1](https://gitlab.com/gitlab-org/duo-ui/commit/7e296d15d82d9edc5f3216c1f557861d6ba37272))
14
+ * trustedUrls validation and comments ([66b1307](https://gitlab.com/gitlab-org/duo-ui/commit/66b1307938f07d50792fcf3d95337512846855fc))
15
+
1
16
  ## [15.0.5](https://gitlab.com/gitlab-org/duo-ui/compare/v15.0.4...v15.0.5) (2025-12-07)
2
17
 
3
18
 
@@ -284,6 +284,17 @@ var script = {
284
284
  type: String,
285
285
  required: false,
286
286
  default: () => ''
287
+ },
288
+ /**
289
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
290
+ * that are allowed to render as clickable links in markdown content.
291
+ * Links to other domains will not be clickable.
292
+ */
293
+ trustedUrls: {
294
+ type: Array,
295
+ required: false,
296
+ default: () => [],
297
+ validator: urls => urls.every(url => typeof url === 'string')
287
298
  }
288
299
  },
289
300
  data() {
@@ -710,7 +721,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
710
721
  },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:{
711
722
  'resizable-content': _vm.shouldRenderResizable,
712
723
  'duo-chat-drawer': !_vm.shouldRenderResizable,
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":[
724
+ },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,"trusted-urls":_vm.trustedUrls},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":[
714
725
  'gl-absolute',
715
726
  '!gl-h-full',
716
727
  'gl-rounded-br-none',
@@ -317,6 +317,17 @@ var script = {
317
317
  type: Boolean,
318
318
  required: false,
319
319
  default: true
320
+ },
321
+ /**
322
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
323
+ * that are allowed to render as clickable links in markdown content.
324
+ * Links to other domains will not be clickable.
325
+ */
326
+ trustedUrls: {
327
+ type: Array,
328
+ required: false,
329
+ default: () => [],
330
+ validator: urls => urls.every(url => typeof url === 'string')
320
331
  }
321
332
  },
322
333
  data() {
@@ -746,9 +757,9 @@ const __vue_script__ = script;
746
757
  /* template */
747
758
  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
759
  'duo-chat-history gl-px-4',
749
- _vm.$scopedSlots['custom-empty-state']
760
+ _vm.$scopedSlots['custom-empty-state'] && !_vm.hasMessages && !_vm.isLoading
750
761
  ? '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":[
762
+ : '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,"trusted-urls":_vm.trustedUrls},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-container"},[_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)]})],2)]:_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":[
752
763
  '!gl-h-full',
753
764
  '!gl-bg-transparent',
754
765
  '!gl-py-4',
@@ -47,12 +47,15 @@ var script = {
47
47
  default: true
48
48
  },
49
49
  /**
50
- * Base URL of the GitLab instance.
50
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
51
+ * that are allowed to render as clickable links in markdown content.
52
+ * Links to other domains will not be clickable.
51
53
  */
52
54
  trustedUrls: {
53
55
  type: Array,
54
56
  required: false,
55
- default: () => []
57
+ default: () => [],
58
+ validator: urls => urls.every(url => typeof url === 'string')
56
59
  },
57
60
  /**
58
61
  * Whether the chat should show the feedback link on the assistant messages.
@@ -67,12 +67,15 @@ var script = {
67
67
  required: true
68
68
  },
69
69
  /**
70
- * Base URL of the GitLab instance.
70
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
71
+ * that are allowed to render as clickable links in markdown content.
72
+ * Links to other domains will not be clickable.
71
73
  */
72
74
  trustedUrls: {
73
75
  type: Array,
74
76
  required: false,
75
- default: () => []
77
+ default: () => [],
78
+ validator: urls => urls.every(url => typeof url === 'string')
76
79
  },
77
80
  /**
78
81
  * Whether the chat should show the feedback link on the assistant messages.
@@ -136,17 +139,27 @@ var script = {
136
139
  if (this.message.contentHtml) {
137
140
  return this.message.contentHtml;
138
141
  }
139
- return this.renderMarkdown(this.message.content, this.trustedUrls);
142
+ return this.renderMarkdown(this.message.content, {
143
+ trustedUrls: this.trustedUrls
144
+ });
140
145
  },
141
146
  messageContent() {
142
147
  if (this.isAssistantMessage && this.isChunk) {
143
- return this.renderMarkdown(concatUntilEmpty(this.messageChunks), this.trustedUrls);
148
+ return this.renderMarkdown(concatUntilEmpty(this.messageChunks), {
149
+ trustedUrls: this.trustedUrls
150
+ });
144
151
  }
145
- return this.renderMarkdown(this.defaultContent, this.trustedUrls) || this.renderMarkdown(concatUntilEmpty(this.message.chunks), this.trustedUrls);
152
+ return this.renderMarkdown(this.defaultContent, {
153
+ trustedUrls: this.trustedUrls
154
+ }) || this.renderMarkdown(concatUntilEmpty(this.message.chunks), {
155
+ trustedUrls: this.trustedUrls
156
+ });
146
157
  },
147
158
  renderedError() {
148
159
  var _this$message$errors;
149
- return this.renderMarkdown(((_this$message$errors = this.message.errors) === null || _this$message$errors === void 0 ? void 0 : _this$message$errors.join('; ')) || '', this.trustedUrls);
160
+ return this.renderMarkdown(((_this$message$errors = this.message.errors) === null || _this$message$errors === void 0 ? void 0 : _this$message$errors.join('; ')) || '', {
161
+ trustedUrls: this.trustedUrls
162
+ });
150
163
  },
151
164
  error() {
152
165
  var _this$message, _this$message$errors2;
@@ -242,12 +242,15 @@ var script = {
242
242
  default: false
243
243
  },
244
244
  /**
245
- * Base URL of the GitLab instance.
245
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
246
+ * that are allowed to render as clickable links in markdown content.
247
+ * Links to other domains will not be clickable.
246
248
  */
247
249
  trustedUrls: {
248
250
  type: Array,
249
251
  required: false,
250
- default: () => []
252
+ default: () => [],
253
+ validator: urls => urls.every(url => typeof url === 'string')
251
254
  },
252
255
  /*
253
256
  * The preferred locale for the chat interface.
@@ -219,12 +219,15 @@ var script = {
219
219
  default: false
220
220
  },
221
221
  /**
222
- * Base URL of the GitLab instance.
222
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
223
+ * that are allowed to render as clickable links in markdown content.
224
+ * Links to other domains will not be clickable.
223
225
  */
224
226
  trustedUrls: {
225
227
  type: Array,
226
228
  required: false,
227
- default: () => []
229
+ default: () => [],
230
+ validator: urls => urls.every(url => typeof url === 'string')
228
231
  },
229
232
  /*
230
233
  * The preferred locale for the chat interface.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/duo-ui",
3
- "version": "15.0.5",
3
+ "version": "15.0.7",
4
4
  "description": "Duo UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -345,6 +345,17 @@ export default {
345
345
  required: false,
346
346
  default: () => '',
347
347
  },
348
+ /**
349
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
350
+ * that are allowed to render as clickable links in markdown content.
351
+ * Links to other domains will not be clickable.
352
+ */
353
+ trustedUrls: {
354
+ type: Array,
355
+ required: false,
356
+ default: () => [],
357
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
358
+ },
348
359
  },
349
360
  data() {
350
361
  return {
@@ -871,6 +882,7 @@ export default {
871
882
  :with-feedback="withFeedback"
872
883
  :is-tool-approval-processing="isToolApprovalProcessing"
873
884
  :working-directory="workingDirectory"
885
+ :trusted-urls="trustedUrls"
874
886
  @track-feedback="onTrackFeedback"
875
887
  @insert-code-snippet="onInsertCodeSnippet"
876
888
  @copy-code-snippet="onCopyCodeSnippet"
@@ -381,6 +381,17 @@ export default {
381
381
  required: false,
382
382
  default: true,
383
383
  },
384
+ /**
385
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
386
+ * that are allowed to render as clickable links in markdown content.
387
+ * Links to other domains will not be clickable.
388
+ */
389
+ trustedUrls: {
390
+ type: Array,
391
+ required: false,
392
+ default: () => [],
393
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
394
+ },
384
395
  },
385
396
  data() {
386
397
  return {
@@ -880,7 +891,7 @@ export default {
880
891
  data-testid="chat-messages"
881
892
  :class="[
882
893
  'duo-chat-history gl-px-4',
883
- $scopedSlots['custom-empty-state']
894
+ $scopedSlots['custom-empty-state'] && !hasMessages && !isLoading
884
895
  ? 'gl-flex gl-flex-1 gl-items-center gl-justify-center'
885
896
  : 'gl-mt-auto gl-pb-4 gl-pt-6',
886
897
  ]"
@@ -894,6 +905,7 @@ export default {
894
905
  :with-feedback="withFeedback"
895
906
  :is-tool-approval-processing="isToolApprovalProcessing"
896
907
  :working-directory="workingDirectory"
908
+ :trusted-urls="trustedUrls"
897
909
  @track-feedback="onTrackFeedback"
898
910
  @insert-code-snippet="onInsertCodeSnippet"
899
911
  @copy-code-snippet="onCopyCodeSnippet"
@@ -904,42 +916,44 @@ export default {
904
916
  @open-file-path="onOpenFilePath"
905
917
  />
906
918
  <template v-if="!hasMessages && !isLoading">
907
- <slot name="custom-empty-state">
908
- <div
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"
912
- >
919
+ <div key="empty-state-container">
920
+ <slot name="custom-empty-state">
913
921
  <div
914
- class="gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"
922
+ key="empty-state-message"
923
+ class="duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-default gl-break-anywhere"
924
+ data-testid="gl-duo-chat-empty-state"
915
925
  >
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"
926
+ <div
927
+ class="gl-mb-[3.75rem] gl-flex gl-flex-col gl-items-center gl-justify-center gl-gap-3 gl-text-center"
932
928
  >
933
- {{ emptyStateSubText }}
934
- </p>
929
+ <h1 class="gl-my-0 gl-text-[3.5rem]" data-testid="gl-duo-chat-empty-state-emoji">
930
+ {{ $options.i18n.CHAT_EMPTY_STATE_EMOJI }}
931
+ </h1>
932
+ <h2
933
+ v-if="agentName"
934
+ class="gl-heading-2 gl-my-0"
935
+ data-testid="gl-duo-chat-empty-state-greeting"
936
+ >
937
+ {{ emptyStateGreeting }}
938
+ </h2>
939
+ <h2 class="gl-my-0 gl-text-size-h2" data-testid="gl-duo-chat-empty-state-title">
940
+ {{ emptyStateMainText }}
941
+ </h2>
942
+ <p
943
+ class="gl-text-base gl-text-subtle"
944
+ data-testid="gl-duo-chat-empty-state-subtitle"
945
+ >
946
+ {{ emptyStateSubText }}
947
+ </p>
948
+ </div>
949
+ <duo-chat-predefined-prompts
950
+ key="predefined-prompts"
951
+ :prompts="predefinedPrompts"
952
+ @click="sendPredefinedPrompt"
953
+ />
935
954
  </div>
936
- <duo-chat-predefined-prompts
937
- key="predefined-prompts"
938
- :prompts="predefinedPrompts"
939
- @click="sendPredefinedPrompt"
940
- />
941
- </div>
942
- </slot>
955
+ </slot>
956
+ </div>
943
957
  </template>
944
958
  <duo-chat-loader v-if="isLoading" key="loader" :tool-name="toolName" />
945
959
  <div key="anchor" ref="anchor" class="scroll-anchor"></div>
@@ -49,12 +49,15 @@ export default {
49
49
  default: true,
50
50
  },
51
51
  /**
52
- * Base URL of the GitLab instance.
52
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
53
+ * that are allowed to render as clickable links in markdown content.
54
+ * Links to other domains will not be clickable.
53
55
  */
54
56
  trustedUrls: {
55
57
  type: Array,
56
58
  required: false,
57
59
  default: () => [],
60
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
58
61
  },
59
62
  /**
60
63
  * Whether the chat should show the feedback link on the assistant messages.
@@ -82,12 +82,15 @@ export default {
82
82
  required: true,
83
83
  },
84
84
  /**
85
- * Base URL of the GitLab instance.
85
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
86
+ * that are allowed to render as clickable links in markdown content.
87
+ * Links to other domains will not be clickable.
86
88
  */
87
89
  trustedUrls: {
88
90
  type: Array,
89
91
  required: false,
90
92
  default: () => [],
93
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
91
94
  },
92
95
  /**
93
96
  * Whether the chat should show the feedback link on the assistant messages.
@@ -149,20 +152,26 @@ export default {
149
152
  return this.message.contentHtml;
150
153
  }
151
154
 
152
- return this.renderMarkdown(this.message.content, this.trustedUrls);
155
+ return this.renderMarkdown(this.message.content, { trustedUrls: this.trustedUrls });
153
156
  },
154
157
  messageContent() {
155
158
  if (this.isAssistantMessage && this.isChunk) {
156
- return this.renderMarkdown(concatUntilEmpty(this.messageChunks), this.trustedUrls);
159
+ return this.renderMarkdown(concatUntilEmpty(this.messageChunks), {
160
+ trustedUrls: this.trustedUrls,
161
+ });
157
162
  }
158
163
 
159
164
  return (
160
- this.renderMarkdown(this.defaultContent, this.trustedUrls) ||
161
- this.renderMarkdown(concatUntilEmpty(this.message.chunks), this.trustedUrls)
165
+ this.renderMarkdown(this.defaultContent, { trustedUrls: this.trustedUrls }) ||
166
+ this.renderMarkdown(concatUntilEmpty(this.message.chunks), {
167
+ trustedUrls: this.trustedUrls,
168
+ })
162
169
  );
163
170
  },
164
171
  renderedError() {
165
- return this.renderMarkdown(this.message.errors?.join('; ') || '', this.trustedUrls);
172
+ return this.renderMarkdown(this.message.errors?.join('; ') || '', {
173
+ trustedUrls: this.trustedUrls,
174
+ });
166
175
  },
167
176
  error() {
168
177
  return Boolean(this.message?.errors?.length) && this.message.errors.join('; ');
@@ -297,12 +297,15 @@ export default {
297
297
  default: false,
298
298
  },
299
299
  /**
300
- * Base URL of the GitLab instance.
300
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
301
+ * that are allowed to render as clickable links in markdown content.
302
+ * Links to other domains will not be clickable.
301
303
  */
302
304
  trustedUrls: {
303
305
  type: Array,
304
306
  required: false,
305
307
  default: () => [],
308
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
306
309
  },
307
310
  /*
308
311
  * The preferred locale for the chat interface.
@@ -279,12 +279,15 @@ export default {
279
279
  default: false,
280
280
  },
281
281
  /**
282
- * Base URL of the GitLab instance.
282
+ * Array of trusted hostnames (e.g., ['gitlab.com', 'example.com'])
283
+ * that are allowed to render as clickable links in markdown content.
284
+ * Links to other domains will not be clickable.
283
285
  */
284
286
  trustedUrls: {
285
287
  type: Array,
286
288
  required: false,
287
289
  default: () => [],
290
+ validator: (urls) => urls.every((url) => typeof url === 'string'),
288
291
  },
289
292
  /*
290
293
  * The preferred locale for the chat interface.