@gitlab/duo-ui 10.21.0 → 10.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/components/agentic_chat/agentic_duo_chat.js +18 -37
- package/dist/components/chat/components/duo_chat_header/duo_chat_header.js +4 -4
- package/dist/components/chat/duo_chat.js +35 -54
- package/dist/components/ui/duo_layout/duo_layout.js +100 -0
- package/dist/components/ui/side_rail/side_rail.js +67 -0
- package/dist/components.css +1 -1
- package/dist/components.css.map +1 -1
- package/dist/index.js +2 -0
- package/dist/tailwind.css +1 -1
- package/dist/tailwind.css.map +1 -1
- package/package.json +2 -1
- package/src/components/agentic_chat/agentic_duo_chat.vue +210 -244
- package/src/components/chat/components/duo_chat_header/duo_chat_header.vue +24 -22
- package/src/components/chat/duo_chat.scss +1 -2
- package/src/components/chat/duo_chat.vue +214 -238
- package/src/components/ui/duo_layout/duo_layout.md +0 -0
- package/src/components/ui/duo_layout/duo_layout.vue +95 -0
- package/src/components/ui/side_rail/side_rail.vue +56 -0
- package/src/index.js +2 -0
- package/translations.js +5 -4
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import throttle from 'lodash/throttle';
|
|
3
|
-
import VueResizable from 'vue-resizable';
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
5
|
GlButton,
|
|
7
6
|
GlDropdownItem,
|
|
8
7
|
GlCard,
|
|
9
|
-
GlAlert,
|
|
10
|
-
GlFormInputGroup,
|
|
11
8
|
GlFormTextarea,
|
|
12
9
|
GlForm,
|
|
13
|
-
GlExperimentBadge,
|
|
14
10
|
GlSafeHtmlDirective as SafeHtml,
|
|
15
11
|
} from '@gitlab/ui';
|
|
16
12
|
|
|
@@ -38,7 +34,7 @@ export const i18n = {
|
|
|
38
34
|
CHAT_HISTORY_TITLE: translate('AgenticDuoChat.chatHistoryTitle', 'Chat history'),
|
|
39
35
|
CHAT_DISCLAMER: translate(
|
|
40
36
|
'AgenticDuoChat.chatDisclamer',
|
|
41
|
-
'
|
|
37
|
+
'Responses may be inaccurate. Verify before use.'
|
|
42
38
|
),
|
|
43
39
|
CHAT_EMPTY_STATE_TITLE: translate(
|
|
44
40
|
'AgenticDuoChat.chatEmptyStateTitle',
|
|
@@ -46,6 +42,10 @@ export const i18n = {
|
|
|
46
42
|
),
|
|
47
43
|
CHAT_PROMPT_PLACEHOLDER_DEFAULT: translate(
|
|
48
44
|
'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,11 +102,8 @@ export default {
|
|
|
102
102
|
name: 'DuoChat',
|
|
103
103
|
components: {
|
|
104
104
|
GlButton,
|
|
105
|
-
GlAlert,
|
|
106
|
-
GlFormInputGroup,
|
|
107
105
|
GlFormTextarea,
|
|
108
106
|
GlForm,
|
|
109
|
-
GlExperimentBadge,
|
|
110
107
|
DuoChatLoader,
|
|
111
108
|
DuoChatPredefinedPrompts,
|
|
112
109
|
DuoChatConversation,
|
|
@@ -114,7 +111,6 @@ export default {
|
|
|
114
111
|
DuoChatThreads,
|
|
115
112
|
GlCard,
|
|
116
113
|
GlDropdownItem,
|
|
117
|
-
VueResizable,
|
|
118
114
|
},
|
|
119
115
|
directives: {
|
|
120
116
|
SafeHtml,
|
|
@@ -343,7 +339,6 @@ export default {
|
|
|
343
339
|
},
|
|
344
340
|
data() {
|
|
345
341
|
return {
|
|
346
|
-
isHidden: false,
|
|
347
342
|
prompt: '',
|
|
348
343
|
scrolledToBottom: true,
|
|
349
344
|
activeCommandIndex: 0,
|
|
@@ -461,7 +456,6 @@ export default {
|
|
|
461
456
|
if (!loading && !this.isStreaming) {
|
|
462
457
|
this.canSubmit = true; // Re-enable submit button when loading stops
|
|
463
458
|
}
|
|
464
|
-
this.isHidden = false;
|
|
465
459
|
},
|
|
466
460
|
isStreaming(streaming) {
|
|
467
461
|
if (!streaming && !this.isLoading) {
|
|
@@ -502,14 +496,10 @@ export default {
|
|
|
502
496
|
this.focusChatInput();
|
|
503
497
|
});
|
|
504
498
|
},
|
|
505
|
-
updateSize(e) {
|
|
506
|
-
this.$emit('chat-resize', e);
|
|
507
|
-
},
|
|
508
499
|
compositionEnd() {
|
|
509
500
|
this.compositionJustEnded = true;
|
|
510
501
|
},
|
|
511
502
|
hideChat() {
|
|
512
|
-
this.isHidden = true;
|
|
513
503
|
/**
|
|
514
504
|
* Emitted when clicking the cross in the title and the chat gets closed.
|
|
515
505
|
*/
|
|
@@ -765,245 +755,221 @@ export default {
|
|
|
765
755
|
};
|
|
766
756
|
</script>
|
|
767
757
|
<template>
|
|
768
|
-
<
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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"
|
|
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"
|
|
785
763
|
>
|
|
786
|
-
<
|
|
787
|
-
v-if="
|
|
788
|
-
|
|
789
|
-
:
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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"
|
|
796
790
|
>
|
|
797
|
-
<duo-chat-
|
|
798
|
-
v-if="
|
|
799
|
-
|
|
800
|
-
:
|
|
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"
|
|
791
|
+
<duo-chat-threads
|
|
792
|
+
v-if="shouldShowThreadList"
|
|
793
|
+
:threads="threadList"
|
|
794
|
+
:preferred-locale="preferredLocale"
|
|
813
795
|
@new-chat="onNewChat"
|
|
796
|
+
@select-thread="onSelectThread"
|
|
797
|
+
@delete-thread="onDeleteThread"
|
|
814
798
|
@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"
|
|
815
806
|
>
|
|
816
|
-
<
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
@
|
|
831
|
-
@
|
|
832
|
-
@
|
|
833
|
-
@close="hideChat"
|
|
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"
|
|
834
824
|
/>
|
|
835
|
-
<
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
<!--
|
|
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>
|
|
840
|
+
</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
|
+
<!--
|
|
889
854
|
@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" ...`
|
|
890
855
|
-->
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
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>
|
|
898
873
|
</div>
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
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"
|
|
904
880
|
>
|
|
905
|
-
<gl-
|
|
906
|
-
v-
|
|
907
|
-
|
|
908
|
-
class="
|
|
909
|
-
|
|
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)"
|
|
910
887
|
>
|
|
911
|
-
<gl-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
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"
|
|
917
930
|
>
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
</
|
|
925
|
-
</
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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>
|
|
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>
|
|
1009
975
|
</template>
|
|
@@ -3,10 +3,10 @@ import Vue from 'vue';
|
|
|
3
3
|
import {
|
|
4
4
|
GlAlert,
|
|
5
5
|
GlBadge,
|
|
6
|
+
GlAvatar,
|
|
6
7
|
GlButton,
|
|
7
8
|
GlDropdown,
|
|
8
9
|
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,
|
|
46
47
|
GlButton,
|
|
47
48
|
GlDropdown,
|
|
48
49
|
GlDropdownItem,
|
|
49
|
-
GlExperimentBadge,
|
|
50
50
|
GlDisclosureDropdown,
|
|
51
51
|
},
|
|
52
52
|
directives: {
|
|
@@ -140,19 +140,31 @@ 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>
|
|
143
162
|
<div class="drawer-title gl-flex gl-items-center gl-justify-start gl-p-5">
|
|
144
|
-
<div class="gl-flex-1 gl-overflow-hidden">
|
|
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" />
|
|
145
165
|
<div class="gl-flex gl-items-center">
|
|
146
|
-
<h3 class="gl-my-0 gl-text-
|
|
147
|
-
<gl-experiment-badge v-if="badgeType" :type="badgeType" container-id="chat-component" />
|
|
166
|
+
<h3 class="gl-my-0 gl-text-[0.875rem]">{{ title }}</h3>
|
|
148
167
|
</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>
|
|
156
168
|
</div>
|
|
157
169
|
|
|
158
170
|
<div class="gl-flex gl-gap-3">
|
|
@@ -242,16 +254,6 @@ export default {
|
|
|
242
254
|
</span>
|
|
243
255
|
</gl-dropdown-item>
|
|
244
256
|
</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
|
-
/>
|
|
255
257
|
</div>
|
|
256
258
|
</div>
|
|
257
259
|
|
|
@@ -141,7 +141,6 @@ $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);
|
|
145
144
|
|
|
146
145
|
&:focus-within {
|
|
147
146
|
@include gl-focus($color: var(--gl-control-border-color-focus));
|
|
@@ -152,7 +151,6 @@ $drawer-width: 400px;
|
|
|
152
151
|
resize: none;
|
|
153
152
|
max-height: 240px;
|
|
154
153
|
padding-right: 40px;
|
|
155
|
-
border-radius: px-to-rem(20px);
|
|
156
154
|
}
|
|
157
155
|
|
|
158
156
|
&::after {
|
|
@@ -160,6 +158,7 @@ $drawer-width: 400px;
|
|
|
160
158
|
@apply gl-invisible;
|
|
161
159
|
@apply gl-p-4;
|
|
162
160
|
@apply gl-font-regular;
|
|
161
|
+
@apply gl-absolute;
|
|
163
162
|
padding-right: 40px;
|
|
164
163
|
word-break: break-word;
|
|
165
164
|
}
|