@gitlab/duo-ui 10.23.0 → 10.23.1

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.
@@ -1,12 +1,16 @@
1
1
  <script>
2
2
  import throttle from 'lodash/throttle';
3
+ import VueResizable from 'vue-resizable';
3
4
 
4
5
  import {
5
6
  GlButton,
6
7
  GlDropdownItem,
7
8
  GlCard,
9
+ GlAlert,
10
+ GlFormInputGroup,
8
11
  GlFormTextarea,
9
12
  GlForm,
13
+ GlExperimentBadge,
10
14
  GlSafeHtmlDirective as SafeHtml,
11
15
  } from '@gitlab/ui';
12
16
 
@@ -34,7 +38,7 @@ export const i18n = {
34
38
  CHAT_HISTORY_TITLE: translate('AgenticDuoChat.chatHistoryTitle', 'Chat history'),
35
39
  CHAT_DISCLAMER: translate(
36
40
  'AgenticDuoChat.chatDisclamer',
37
- 'Responses may be inaccurate. Verify before use.'
41
+ 'Chat can autonomously change code. Responses and changes can be inaccurate. Review carefully.'
38
42
  ),
39
43
  CHAT_EMPTY_STATE_TITLE: translate(
40
44
  'AgenticDuoChat.chatEmptyStateTitle',
@@ -42,10 +46,6 @@ export const i18n = {
42
46
  ),
43
47
  CHAT_PROMPT_PLACEHOLDER_DEFAULT: translate(
44
48
  'AgenticDuoChat.chatPromptPlaceholderDefault',
45
- "Let's work through this together..."
46
- ),
47
- CHAT_MODEL_PLACEHOLDER: translate(
48
- 'AgenticDuoChat.chatModelPlaceholder',
49
49
  'GitLab Duo Agentic Chat'
50
50
  ),
51
51
  CHAT_PROMPT_PLACEHOLDER_WITH_COMMANDS: translate(
@@ -102,8 +102,11 @@ export default {
102
102
  name: 'DuoChat',
103
103
  components: {
104
104
  GlButton,
105
+ GlAlert,
106
+ GlFormInputGroup,
105
107
  GlFormTextarea,
106
108
  GlForm,
109
+ GlExperimentBadge,
107
110
  DuoChatLoader,
108
111
  DuoChatPredefinedPrompts,
109
112
  DuoChatConversation,
@@ -111,6 +114,7 @@ export default {
111
114
  DuoChatThreads,
112
115
  GlCard,
113
116
  GlDropdownItem,
117
+ VueResizable,
114
118
  },
115
119
  directives: {
116
120
  SafeHtml,
@@ -339,6 +343,7 @@ export default {
339
343
  },
340
344
  data() {
341
345
  return {
346
+ isHidden: false,
342
347
  prompt: '',
343
348
  scrolledToBottom: true,
344
349
  activeCommandIndex: 0,
@@ -456,6 +461,7 @@ export default {
456
461
  if (!loading && !this.isStreaming) {
457
462
  this.canSubmit = true; // Re-enable submit button when loading stops
458
463
  }
464
+ this.isHidden = false;
459
465
  },
460
466
  isStreaming(streaming) {
461
467
  if (!streaming && !this.isLoading) {
@@ -496,10 +502,14 @@ export default {
496
502
  this.focusChatInput();
497
503
  });
498
504
  },
505
+ updateSize(e) {
506
+ this.$emit('chat-resize', e);
507
+ },
499
508
  compositionEnd() {
500
509
  this.compositionJustEnded = true;
501
510
  },
502
511
  hideChat() {
512
+ this.isHidden = true;
503
513
  /**
504
514
  * Emitted when clicking the cross in the title and the chat gets closed.
505
515
  */
@@ -755,221 +765,245 @@ export default {
755
765
  };
756
766
  </script>
757
767
  <template>
758
- <div
759
- id="chat-component"
760
- class="markdown-code-block duo-chat gl-bottom-0 gl-flex gl-max-h-full gl-flex-col"
761
- role="complementary"
762
- data-testid="chat-component"
768
+ <component
769
+ :is="shouldRenderResizable ? 'vue-resizable' : 'div'"
770
+ :width="shouldRenderResizable ? dimensions.width : null"
771
+ :height="shouldRenderResizable ? dimensions.height : null"
772
+ :max-width="shouldRenderResizable ? dimensions.maxWidth : null"
773
+ :max-height="shouldRenderResizable ? dimensions.maxHeight : null"
774
+ :min-width="shouldRenderResizable ? dimensions.minWidth : null"
775
+ :left="shouldRenderResizable ? dimensions.left : null"
776
+ :top="shouldRenderResizable ? dimensions.top : null"
777
+ :fit-parent="true"
778
+ :min-height="shouldRenderResizable ? dimensions.minHeight : null"
779
+ :class="{
780
+ 'duo-chat-resizable': shouldRenderResizable,
781
+ 'non-resizable-wrapper': !shouldRenderResizable,
782
+ }"
783
+ :active="shouldRenderResizable ? ['l', 't', 'lt'] : null"
784
+ @resize:end="updateSize"
763
785
  >
764
- <duo-chat-header
765
- v-if="showHeader"
766
- ref="header"
767
- :active-thread-id="activeThreadId"
768
- :title="isMultithreaded && currentView === 'list' ? $options.i18n.CHAT_HISTORY_TITLE : title"
769
- :subtitle="activeThreadTitleForView"
770
- :error="error"
771
- :is-multithreaded="isMultithreaded"
772
- :current-view="currentView"
773
- :should-render-resizable="shouldRenderResizable"
774
- :badge-type="isMultithreaded ? null : badgeType"
775
- :session-id="sessionId"
776
- :agents="agents"
777
- @go-back="onGoBack"
778
- @new-chat="onNewChat"
779
- @close="hideChat"
780
- >
781
- <template #subheader>
782
- <slot name="subheader"></slot>
783
- </template>
784
- </duo-chat-header>
785
-
786
- <div
787
- class="gl-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit"
788
- data-testid="chat-history"
789
- @scroll="handleScrollingThrottled"
786
+ <aside
787
+ v-if="!isHidden"
788
+ id="chat-component"
789
+ :class="{
790
+ 'resizable-content': shouldRenderResizable,
791
+ 'duo-chat-drawer': !shouldRenderResizable,
792
+ }"
793
+ class="markdown-code-block duo-chat gl-bottom-0 gl-flex gl-max-h-full gl-flex-col"
794
+ role="complementary"
795
+ data-testid="chat-component"
790
796
  >
791
- <duo-chat-threads
792
- v-if="shouldShowThreadList"
793
- :threads="threadList"
794
- :preferred-locale="preferredLocale"
797
+ <duo-chat-header
798
+ v-if="showHeader"
799
+ ref="header"
800
+ :active-thread-id="activeThreadId"
801
+ :title="
802
+ isMultithreaded && currentView === 'list' ? $options.i18n.CHAT_HISTORY_TITLE : title
803
+ "
804
+ :subtitle="activeThreadTitleForView"
805
+ :error="error"
806
+ :is-multithreaded="isMultithreaded"
807
+ :current-view="currentView"
808
+ :should-render-resizable="shouldRenderResizable"
809
+ :badge-type="isMultithreaded ? null : badgeType"
810
+ :session-id="sessionId"
811
+ :agents="agents"
812
+ @go-back="onGoBack"
795
813
  @new-chat="onNewChat"
796
- @select-thread="onSelectThread"
797
- @delete-thread="onDeleteThread"
798
814
  @close="hideChat"
799
- />
800
- <transition-group
801
- v-else
802
- mode="out-in"
803
- tag="section"
804
- name="message"
805
- class="duo-chat-history gl-mt-auto gl-p-5"
806
815
  >
807
- <duo-chat-conversation
808
- v-for="(conversation, index) in conversations"
809
- :key="`conversation-${index}`"
810
- :enable-code-insertion="enableCodeInsertion"
811
- :messages="conversation"
812
- :show-delimiter="index > 0"
813
- :with-feedback="withFeedback"
814
- :is-tool-approval-processing="isToolApprovalProcessing"
815
- :working-directory="workingDirectory"
816
- @track-feedback="onTrackFeedback"
817
- @insert-code-snippet="onInsertCodeSnippet"
818
- @copy-code-snippet="onCopyCodeSnippet"
819
- @copy-message="onCopyMessage"
820
- @get-context-item-content="onGetContextItemContent"
821
- @approve-tool="onApproveToolCall"
822
- @deny-tool="onDenyToolCall"
823
- @open-file-path="onOpenFilePath"
824
- />
825
- <template v-if="!hasMessages && !isLoading">
826
- <div
827
- key="empty-state-message"
828
- class="duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-gray-900 gl-break-anywhere"
829
- data-testid="gl-duo-chat-empty-state"
830
- >
831
- <p v-if="emptyStateTitle" data-testid="gl-duo-chat-empty-state-title" class="gl-m-0">
832
- {{ emptyStateTitle }}
833
- </p>
834
- <duo-chat-predefined-prompts
835
- key="predefined-prompts"
836
- :prompts="predefinedPrompts"
837
- @click="sendPredefinedPrompt"
838
- />
839
- </div>
816
+ <template #subheader>
817
+ <slot name="subheader"></slot>
840
818
  </template>
841
- <duo-chat-loader v-if="isLoading" key="loader" :tool-name="toolName" />
842
- <div key="anchor" ref="anchor" class="scroll-anchor"></div>
843
- </transition-group>
844
- </div>
845
- <footer
846
- v-if="isChatAvailable && !shouldShowThreadList"
847
- data-testid="chat-footer"
848
- class="duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0 gl-border-0 gl-bg-default gl-pb-3"
849
- :class="{ 'duo-chat-drawer-body-scrim-on-footer': !scrolledToBottom }"
850
- >
851
- <gl-form data-testid="chat-prompt-form" @submit.stop.prevent="sendChatPrompt">
852
- <div class="gl-relative gl-max-w-full">
853
- <!--
819
+ </duo-chat-header>
820
+
821
+ <div
822
+ class="gl-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit"
823
+ data-testid="chat-history"
824
+ @scroll="handleScrollingThrottled"
825
+ >
826
+ <duo-chat-threads
827
+ v-if="shouldShowThreadList"
828
+ :threads="threadList"
829
+ :preferred-locale="preferredLocale"
830
+ @new-chat="onNewChat"
831
+ @select-thread="onSelectThread"
832
+ @delete-thread="onDeleteThread"
833
+ @close="hideChat"
834
+ />
835
+ <transition-group
836
+ v-else
837
+ mode="out-in"
838
+ tag="section"
839
+ name="message"
840
+ class="duo-chat-history gl-mt-auto gl-p-5"
841
+ >
842
+ <duo-chat-conversation
843
+ v-for="(conversation, index) in conversations"
844
+ :key="`conversation-${index}`"
845
+ :enable-code-insertion="enableCodeInsertion"
846
+ :messages="conversation"
847
+ :show-delimiter="index > 0"
848
+ :with-feedback="withFeedback"
849
+ :is-tool-approval-processing="isToolApprovalProcessing"
850
+ :working-directory="workingDirectory"
851
+ @track-feedback="onTrackFeedback"
852
+ @insert-code-snippet="onInsertCodeSnippet"
853
+ @copy-code-snippet="onCopyCodeSnippet"
854
+ @copy-message="onCopyMessage"
855
+ @get-context-item-content="onGetContextItemContent"
856
+ @approve-tool="onApproveToolCall"
857
+ @deny-tool="onDenyToolCall"
858
+ @open-file-path="onOpenFilePath"
859
+ />
860
+ <template v-if="!hasMessages && !isLoading">
861
+ <div
862
+ key="empty-state-message"
863
+ class="duo-chat-message gl-rounded-bl-none gl-leading-20 gl-text-gray-900 gl-break-anywhere"
864
+ data-testid="gl-duo-chat-empty-state"
865
+ >
866
+ <p v-if="emptyStateTitle" data-testid="gl-duo-chat-empty-state-title" class="gl-m-0">
867
+ {{ emptyStateTitle }}
868
+ </p>
869
+ <duo-chat-predefined-prompts
870
+ key="predefined-prompts"
871
+ :prompts="predefinedPrompts"
872
+ @click="sendPredefinedPrompt"
873
+ />
874
+ </div>
875
+ </template>
876
+ <duo-chat-loader v-if="isLoading" key="loader" :tool-name="toolName" />
877
+ <div key="anchor" ref="anchor" class="scroll-anchor"></div>
878
+ </transition-group>
879
+ </div>
880
+ <footer
881
+ v-if="isChatAvailable && !shouldShowThreadList"
882
+ data-testid="chat-footer"
883
+ class="duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0 gl-border-0 gl-bg-default gl-pb-3"
884
+ :class="{ 'duo-chat-drawer-body-scrim-on-footer': !scrolledToBottom }"
885
+ >
886
+ <gl-form data-testid="chat-prompt-form" @submit.stop.prevent="sendChatPrompt">
887
+ <div class="gl-relative gl-max-w-full">
888
+ <!--
854
889
  @slot For integrating `<gl-context-items-menu>` component if pinned-context should be available. The following scopedSlot properties are provided: `isOpen`, `onClose`, `setRef`, `focusPrompt`, which should be passed to the `<gl-context-items-menu>` component when rendering, e.g. `<template #context-items-menu="{ isOpen, onClose, setRef, focusPrompt }">` `<duo-chat-context-item-menu :ref="setRef" :open="isOpen" @close="onClose" @focus-prompt="focusPrompt" ...`
855
890
  -->
856
- <slot
857
- name="context-items-menu"
858
- :is-open="contextItemsMenuIsOpen"
859
- :on-close="closeContextItemsMenuOpen"
860
- :set-ref="setContextItemsMenuRef"
861
- :focus-prompt="focusChatInput"
862
- ></slot>
863
- </div>
864
-
865
- <div
866
- class="duo-chat-input 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"
867
- >
868
- <div
869
- class="gl-flex gl-justify-between gl-border-0 gl-border-b-1 gl-border-solid gl-border-[#DCDCDE] gl-px-4 gl-py-4"
870
- >
871
- <div>{{ $options.i18n.CHAT_MODEL_PLACEHOLDER }}</div>
872
- <div><slot name="agentic-switch"></slot></div>
891
+ <slot
892
+ name="context-items-menu"
893
+ :is-open="contextItemsMenuIsOpen"
894
+ :on-close="closeContextItemsMenuOpen"
895
+ :set-ref="setContextItemsMenuRef"
896
+ :focus-prompt="focusChatInput"
897
+ ></slot>
873
898
  </div>
874
- <div :data-value="prompt" class="gl-h-[40px] gl-grow">
875
- <gl-card
876
- v-if="shouldShowSlashCommands"
877
- ref="commands"
878
- class="slash-commands !gl-absolute gl-w-full -gl-translate-y-full gl-list-none gl-pl-0 gl-shadow-md"
879
- body-class="!gl-p-2"
899
+
900
+ <gl-form-input-group>
901
+ <div
902
+ class="duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-align-top"
903
+ :data-value="prompt"
880
904
  >
881
- <gl-dropdown-item
882
- v-for="(command, index) in filteredSlashCommands"
883
- :key="command.name"
884
- :class="{ 'active-command': index === activeCommandIndex }"
885
- @mouseenter.native="activeCommandIndex = index"
886
- @click="selectSlashCommand(index)"
905
+ <gl-card
906
+ v-if="shouldShowSlashCommands"
907
+ ref="commands"
908
+ class="slash-commands !gl-absolute gl-w-full -gl-translate-y-full gl-list-none gl-pl-0 gl-shadow-md"
909
+ body-class="!gl-p-2"
887
910
  >
888
- <span class="gl-flex gl-justify-between">
889
- <span class="gl-block">{{ command.name }}</span>
890
- <small class="gl-pl-3 gl-text-right gl-italic gl-text-subtle">{{
891
- command.description
892
- }}</small>
893
- </span>
894
- </gl-dropdown-item>
895
- </gl-card>
896
-
897
- <gl-form-textarea
898
- ref="prompt"
899
- v-model="prompt"
900
- :disabled="!canSubmit"
901
- data-testid="chat-prompt-input"
902
- :placeholder="inputPlaceholder"
903
- :character-count-limit="maxPromptLength"
904
- :textarea-classes="[
905
- '!gl-h-full',
906
- '!gl-bg-transparent',
907
- '!gl-py-4',
908
- '!gl-shadow-none',
909
- 'form-control',
910
- 'gl-form-input',
911
- 'gl-form-textarea',
912
- { 'gl-truncate': !prompt },
913
- ]"
914
- aria-label="Chat prompt input"
915
- autofocus
916
- @keydown.enter.exact.native.prevent
917
- @keydown.ctrl.z.exact="handleUndo"
918
- @keydown.meta.z.exact="handleUndo"
919
- @keydown.ctrl.shift.z.exact="handleRedo"
920
- @keydown.meta.shift.z.exact="handleRedo"
921
- @keydown.ctrl.y.exact="handleRedo"
922
- @keydown.meta.y.exact="handleRedo"
923
- @keyup.native="onInputKeyup"
924
- @compositionend="compositionEnd"
925
- >
926
- <template #remaining-character-count-text="{ count }">
927
- <span
928
- v-if="count <= promptLengthWarningCount"
929
- class="gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3"
911
+ <gl-dropdown-item
912
+ v-for="(command, index) in filteredSlashCommands"
913
+ :key="command.name"
914
+ :class="{ 'active-command': index === activeCommandIndex }"
915
+ @mouseenter.native="activeCommandIndex = index"
916
+ @click="selectSlashCommand(index)"
930
917
  >
931
- {{ remainingCharacterCountMessage(count) }}
932
- </span>
933
- </template>
934
- <template #character-count-over-limit-text="{ count }">
935
- <span class="gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3">{{
936
- overLimitCharacterCountMessage(count)
937
- }}</span>
938
- </template>
939
- </gl-form-textarea>
940
- </div>
941
- <div class="gl-flex gl-justify-end gl-px-3 gl-pb-3">
942
- <gl-button
943
- v-if="canSubmit"
944
- icon="paper-airplane"
945
- category="primary"
946
- variant="confirm"
947
- class="gl-bottom-2 gl-right-2 gl-ml-auto !gl-rounded-full"
948
- type="submit"
949
- :disabled="isPromptEmpty || !hasValidPrompt"
950
- data-testid="chat-prompt-submit-button"
951
- :aria-label="$options.i18n.CHAT_SUBMIT_LABEL"
952
- />
953
- <gl-button
954
- v-else
955
- icon="stop"
956
- category="primary"
957
- variant="default"
958
- class="gl-bottom-2 gl-right-2 !gl-rounded-full"
959
- data-testid="chat-prompt-cancel-button"
960
- :aria-label="$options.i18n.CHAT_CANCEL_LABEL"
961
- @click="cancelPrompt"
962
- />
963
- </div>
964
- </div>
965
- </gl-form>
966
- <slot name="footer-controls"></slot>
967
- <p
968
- class="gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"
969
- :class="{ 'gl-mt-6 sm:gl-mt-3 sm:gl-max-w-1/2': prompt.length >= maxPromptLengthWarning }"
970
- >
971
- {{ $options.i18n.CHAT_DISCLAMER }}
972
- </p>
973
- </footer>
974
- </div>
918
+ <span class="gl-flex gl-justify-between">
919
+ <span class="gl-block">{{ command.name }}</span>
920
+ <small class="gl-pl-3 gl-text-right gl-italic gl-text-subtle">{{
921
+ command.description
922
+ }}</small>
923
+ </span>
924
+ </gl-dropdown-item>
925
+ </gl-card>
926
+
927
+ <gl-form-textarea
928
+ ref="prompt"
929
+ v-model="prompt"
930
+ :disabled="!canSubmit"
931
+ data-testid="chat-prompt-input"
932
+ :placeholder="inputPlaceholder"
933
+ :character-count-limit="maxPromptLength"
934
+ :textarea-classes="[
935
+ 'gl-absolute',
936
+ '!gl-h-full',
937
+ 'gl-rounded-br-none',
938
+ 'gl-rounded-tr-none',
939
+ '!gl-bg-transparent',
940
+ '!gl-py-4',
941
+ '!gl-shadow-none',
942
+ 'form-control',
943
+ 'gl-form-input',
944
+ 'gl-form-textarea',
945
+ { 'gl-truncate': !prompt },
946
+ ]"
947
+ aria-label="Chat prompt input"
948
+ autofocus
949
+ @keydown.enter.exact.native.prevent
950
+ @keydown.ctrl.z.exact="handleUndo"
951
+ @keydown.meta.z.exact="handleUndo"
952
+ @keydown.ctrl.shift.z.exact="handleRedo"
953
+ @keydown.meta.shift.z.exact="handleRedo"
954
+ @keydown.ctrl.y.exact="handleRedo"
955
+ @keydown.meta.y.exact="handleRedo"
956
+ @keyup.native="onInputKeyup"
957
+ @compositionend="compositionEnd"
958
+ >
959
+ <template #remaining-character-count-text="{ count }">
960
+ <span
961
+ v-if="count <= promptLengthWarningCount"
962
+ class="gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3"
963
+ >
964
+ {{ remainingCharacterCountMessage(count) }}
965
+ </span>
966
+ </template>
967
+ <template #character-count-over-limit-text="{ count }">
968
+ <span class="gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3">{{
969
+ overLimitCharacterCountMessage(count)
970
+ }}</span>
971
+ </template>
972
+ </gl-form-textarea>
973
+ </div>
974
+ <template #append>
975
+ <gl-button
976
+ v-if="canSubmit"
977
+ icon="paper-airplane"
978
+ category="primary"
979
+ variant="confirm"
980
+ class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full"
981
+ type="submit"
982
+ :disabled="isPromptEmpty || !hasValidPrompt"
983
+ data-testid="chat-prompt-submit-button"
984
+ :aria-label="$options.i18n.CHAT_SUBMIT_LABEL"
985
+ />
986
+ <gl-button
987
+ v-else
988
+ icon="stop"
989
+ category="primary"
990
+ variant="default"
991
+ class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full"
992
+ data-testid="chat-prompt-cancel-button"
993
+ :aria-label="$options.i18n.CHAT_CANCEL_LABEL"
994
+ @click="cancelPrompt"
995
+ />
996
+ </template>
997
+ </gl-form-input-group>
998
+ </gl-form>
999
+ <slot name="footer-controls"></slot>
1000
+ <p
1001
+ class="gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"
1002
+ :class="{ 'gl-mt-6 sm:gl-mt-3 sm:gl-max-w-1/2': prompt.length >= maxPromptLengthWarning }"
1003
+ >
1004
+ {{ $options.i18n.CHAT_DISCLAMER }}
1005
+ </p>
1006
+ </footer>
1007
+ </aside>
1008
+ </component>
975
1009
  </template>
@@ -3,10 +3,10 @@ import Vue from 'vue';
3
3
  import {
4
4
  GlAlert,
5
5
  GlBadge,
6
- GlAvatar,
7
6
  GlButton,
8
7
  GlDropdown,
9
8
  GlDropdownItem,
9
+ GlExperimentBadge,
10
10
  GlSafeHtmlDirective as SafeHtml,
11
11
  GlTooltipDirective,
12
12
  GlToast,
@@ -43,10 +43,10 @@ export default {
43
43
  components: {
44
44
  GlAlert,
45
45
  GlBadge,
46
- GlAvatar,
47
46
  GlButton,
48
47
  GlDropdown,
49
48
  GlDropdownItem,
49
+ GlExperimentBadge,
50
50
  GlDisclosureDropdown,
51
51
  },
52
52
  directives: {
@@ -140,31 +140,19 @@ export default {
140
140
 
141
141
  <template>
142
142
  <header data-testid="chat-header" class="gl-border-b gl-shrink-0 gl-bg-default gl-p-0">
143
- <div class="gl-border-b gl-flex gl-w-full gl-items-center gl-px-5 gl-py-3">
144
- <h4
145
- v-if="subtitle"
146
- class="gl-mb-0 gl-shrink-0 gl-overflow-hidden gl-text-ellipsis gl-whitespace-nowrap gl-pr-3 gl-text-sm gl-font-normal gl-text-subtle"
147
- data-testid="chat-subtitle"
148
- >
149
- {{ subtitle }}
150
- </h4>
151
- <gl-button
152
- category="tertiary"
153
- variant="default"
154
- icon="close"
155
- size="small"
156
- class="gl-ml-auto"
157
- data-testid="chat-close-button"
158
- :aria-label="$options.i18n.CHAT_CLOSE_LABEL"
159
- @click="$emit('close')"
160
- />
161
- </div>
162
143
  <div class="drawer-title gl-flex gl-items-center gl-justify-start gl-p-5">
163
- <div class="gl-flex gl-flex-1 gl-overflow-hidden">
164
- <gl-avatar :size="32" :entity-name="title" shape="circle" class="gl-mr-3" />
144
+ <div class="gl-flex-1 gl-overflow-hidden">
165
145
  <div class="gl-flex gl-items-center">
166
- <h3 class="gl-my-0 gl-text-[0.875rem]">{{ title }}</h3>
146
+ <h3 class="gl-my-0 gl-text-size-h2">{{ title }}</h3>
147
+ <gl-experiment-badge v-if="badgeType" :type="badgeType" container-id="chat-component" />
167
148
  </div>
149
+ <h4
150
+ v-if="subtitle"
151
+ class="gl-mb-0 gl-overflow-hidden gl-text-ellipsis gl-whitespace-nowrap gl-pr-3 gl-text-sm gl-font-normal gl-text-subtle"
152
+ data-testid="chat-subtitle"
153
+ >
154
+ {{ subtitle }}
155
+ </h4>
168
156
  </div>
169
157
 
170
158
  <div class="gl-flex gl-gap-3">
@@ -254,6 +242,16 @@ export default {
254
242
  </span>
255
243
  </gl-dropdown-item>
256
244
  </gl-dropdown>
245
+ <gl-button
246
+ category="tertiary"
247
+ variant="default"
248
+ icon="close"
249
+ size="small"
250
+ class="gl-ml-auto"
251
+ data-testid="chat-close-button"
252
+ :aria-label="$options.i18n.CHAT_CLOSE_LABEL"
253
+ @click="$emit('close')"
254
+ />
257
255
  </div>
258
256
  </div>
259
257
 
@@ -141,6 +141,7 @@ $drawer-width: 400px;
141
141
  overflow: hidden;
142
142
  background: var(--gl-control-background-color-default);
143
143
  box-shadow: inset 0 0 0 $gl-border-size-1 var(--gl-control-border-color-default);
144
+ border-radius: px-to-rem(20px);
144
145
 
145
146
  &:focus-within {
146
147
  @include gl-focus($color: var(--gl-control-border-color-focus));
@@ -151,6 +152,7 @@ $drawer-width: 400px;
151
152
  resize: none;
152
153
  max-height: 240px;
153
154
  padding-right: 40px;
155
+ border-radius: px-to-rem(20px);
154
156
  }
155
157
 
156
158
  &::after {
@@ -158,7 +160,6 @@ $drawer-width: 400px;
158
160
  @apply gl-invisible;
159
161
  @apply gl-p-4;
160
162
  @apply gl-font-regular;
161
- @apply gl-absolute;
162
163
  padding-right: 40px;
163
164
  word-break: break-word;
164
165
  }