@gitlab/duo-ui 15.10.0 → 15.10.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.
- package/dist/components/agentic_chat/components/agentic_binary_feedback/agentic_binary_feedback.js +24 -147
- package/dist/components/agentic_chat/components/agentic_feedback_panel/agentic_feedback_panel.js +185 -0
- package/dist/components/chat/components/duo_chat_message/duo_chat_message.js +21 -2
- package/dist/components/chat/components/message_action_bar/message_action_bar.js +43 -0
- package/dist/components.css +1 -1
- package/dist/components.css.map +1 -1
- package/dist/tailwind.css +1 -1
- package/dist/tailwind.css.map +1 -1
- package/package.json +1 -1
- package/src/components/agentic_chat/components/agentic_binary_feedback/agentic_binary_feedback.scss +2 -2
- package/src/components/agentic_chat/components/agentic_binary_feedback/agentic_binary_feedback.vue +35 -233
- package/src/components/agentic_chat/components/agentic_feedback_panel/agentic_feedback_panel.vue +228 -0
- package/src/components/chat/components/duo_chat_message/duo_chat_message.vue +64 -33
- package/src/components/chat/components/message_action_bar/message_action_bar.vue +11 -0
package/src/components/agentic_chat/components/agentic_feedback_panel/agentic_feedback_panel.vue
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { GlButton, GlFormTextarea, GlForm } from '@gitlab/ui';
|
|
3
|
+
import { translate } from '@gitlab/ui/dist/utils/i18n';
|
|
4
|
+
|
|
5
|
+
export const i18n = {
|
|
6
|
+
TELL_US_MORE: translate('AgenticBinaryFeedback.tellUsMore', 'Tell us more'),
|
|
7
|
+
SUBMIT: translate('AgenticBinaryFeedback.submit', 'Submit'),
|
|
8
|
+
BACK: translate('AgenticBinaryFeedback.back', 'Back'),
|
|
9
|
+
CLOSE: translate('AgenticBinaryFeedback.close', 'Close'),
|
|
10
|
+
FEEDBACK_PLACEHOLDER: translate('AgenticBinaryFeedback.placeholder', 'Share your feedback...'),
|
|
11
|
+
THUMBS_UP_HEADER: translate('AgenticBinaryFeedback.thumbsUpHeader', 'What made this helpful?'),
|
|
12
|
+
THUMBS_DOWN_HEADER: translate(
|
|
13
|
+
'AgenticBinaryFeedback.thumbsDownHeader',
|
|
14
|
+
'What would have been more helpful?'
|
|
15
|
+
),
|
|
16
|
+
TOO_GENERIC: translate('AgenticBinaryFeedback.tooGeneric', 'Too generic'),
|
|
17
|
+
MISSING_STEPS: translate('AgenticBinaryFeedback.missingSteps', 'Missing steps'),
|
|
18
|
+
WRONG_CONTEXT: translate('AgenticBinaryFeedback.wrongContext', 'Wrong context'),
|
|
19
|
+
OUTDATED_INCORRECT: translate('AgenticBinaryFeedback.outdatedIncorrect', 'Outdated/incorrect'),
|
|
20
|
+
SOLVED_PROBLEM: translate('AgenticBinaryFeedback.solvedProblem', 'Solved my problem'),
|
|
21
|
+
SAVED_TIME: translate('AgenticBinaryFeedback.savedTime', 'Saved me time'),
|
|
22
|
+
GOOD_EXAMPLES: translate('AgenticBinaryFeedback.goodExamples', 'Good examples'),
|
|
23
|
+
ACCURATE_INFO: translate('AgenticBinaryFeedback.accurateInfo', 'Accurate information'),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const PANEL_VIEW = {
|
|
27
|
+
REASON_LIST: 'reason_list',
|
|
28
|
+
TELL_US_MORE: 'tell_us_more',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const thumbsDownReasons = [
|
|
32
|
+
{ key: 'too_generic', label: i18n.TOO_GENERIC },
|
|
33
|
+
{ key: 'missing_steps', label: i18n.MISSING_STEPS },
|
|
34
|
+
{ key: 'wrong_context', label: i18n.WRONG_CONTEXT },
|
|
35
|
+
{ key: 'outdated_incorrect', label: i18n.OUTDATED_INCORRECT },
|
|
36
|
+
{ key: 'tell_us_more', label: i18n.TELL_US_MORE, isCustom: true },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export const thumbsUpReasons = [
|
|
40
|
+
{ key: 'solved_problem', label: i18n.SOLVED_PROBLEM },
|
|
41
|
+
{ key: 'saved_time', label: i18n.SAVED_TIME },
|
|
42
|
+
{ key: 'good_examples', label: i18n.GOOD_EXAMPLES },
|
|
43
|
+
{ key: 'accurate_info', label: i18n.ACCURATE_INFO },
|
|
44
|
+
{ key: 'tell_us_more', label: i18n.TELL_US_MORE, isCustom: true },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const CHARACTER_LIMIT = 140;
|
|
48
|
+
|
|
49
|
+
export default {
|
|
50
|
+
name: 'AgenticFeedbackPanel',
|
|
51
|
+
i18n,
|
|
52
|
+
thumbsUpReasons,
|
|
53
|
+
thumbsDownReasons,
|
|
54
|
+
components: {
|
|
55
|
+
GlButton,
|
|
56
|
+
GlFormTextarea,
|
|
57
|
+
GlForm,
|
|
58
|
+
},
|
|
59
|
+
props: {
|
|
60
|
+
feedbackType: {
|
|
61
|
+
type: String,
|
|
62
|
+
required: true,
|
|
63
|
+
validator: (value) => ['thumbs_up', 'thumbs_down'].includes(value),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
data() {
|
|
67
|
+
return {
|
|
68
|
+
panelView: PANEL_VIEW.REASON_LIST,
|
|
69
|
+
customFeedback: '',
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
computed: {
|
|
73
|
+
isThumbsUp() {
|
|
74
|
+
return this.feedbackType === 'thumbs_up';
|
|
75
|
+
},
|
|
76
|
+
reasonOptions() {
|
|
77
|
+
return this.isThumbsUp ? this.$options.thumbsUpReasons : this.$options.thumbsDownReasons;
|
|
78
|
+
},
|
|
79
|
+
reasonHeader() {
|
|
80
|
+
return this.isThumbsUp
|
|
81
|
+
? this.$options.i18n.THUMBS_UP_HEADER
|
|
82
|
+
: this.$options.i18n.THUMBS_DOWN_HEADER;
|
|
83
|
+
},
|
|
84
|
+
isOverCharacterLimit() {
|
|
85
|
+
return this.customFeedback.length > CHARACTER_LIMIT;
|
|
86
|
+
},
|
|
87
|
+
trimmedFeedback() {
|
|
88
|
+
return this.customFeedback.trim();
|
|
89
|
+
},
|
|
90
|
+
isTextareaValid() {
|
|
91
|
+
return this.trimmedFeedback.length === 0 ? null : !this.isOverCharacterLimit;
|
|
92
|
+
},
|
|
93
|
+
canSubmitCustomFeedback() {
|
|
94
|
+
return this.trimmedFeedback.length > 0 && !this.isOverCharacterLimit;
|
|
95
|
+
},
|
|
96
|
+
characterCountText() {
|
|
97
|
+
const remaining = CHARACTER_LIMIT - this.customFeedback.length;
|
|
98
|
+
if (remaining >= 0) {
|
|
99
|
+
return remaining === 1
|
|
100
|
+
? `${remaining} character remaining`
|
|
101
|
+
: `${remaining} characters remaining`;
|
|
102
|
+
}
|
|
103
|
+
const over = Math.abs(remaining);
|
|
104
|
+
return over === 1 ? `${over} character over limit` : `${over} characters over limit`;
|
|
105
|
+
},
|
|
106
|
+
isReasonListVisible() {
|
|
107
|
+
return this.panelView === PANEL_VIEW.REASON_LIST;
|
|
108
|
+
},
|
|
109
|
+
isTellUsMoreVisible() {
|
|
110
|
+
return this.panelView === PANEL_VIEW.TELL_US_MORE;
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
methods: {
|
|
114
|
+
selectReason(reason) {
|
|
115
|
+
if (reason.isCustom) {
|
|
116
|
+
this.panelView = PANEL_VIEW.TELL_US_MORE;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.$emit('submit', {
|
|
120
|
+
feedbackType: this.feedbackType,
|
|
121
|
+
feedbackReason: reason.key,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
submitCustomFeedback() {
|
|
125
|
+
this.$emit('submit', {
|
|
126
|
+
feedbackType: this.feedbackType,
|
|
127
|
+
feedbackReason: `custom_${this.customFeedback.trim()}`,
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
goBackToReasons() {
|
|
131
|
+
this.panelView = PANEL_VIEW.REASON_LIST;
|
|
132
|
+
this.customFeedback = '';
|
|
133
|
+
},
|
|
134
|
+
close() {
|
|
135
|
+
this.$emit('close');
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<template>
|
|
142
|
+
<div
|
|
143
|
+
class="agentic-feedback-panel gl-border gl-mt-2 gl-rounded-lg gl-border-default gl-bg-subtle gl-p-3"
|
|
144
|
+
data-testid="feedback-reason-dropdown"
|
|
145
|
+
>
|
|
146
|
+
<div class="gl-mb-3 gl-flex gl-items-center gl-justify-between gl-gap-2">
|
|
147
|
+
<p class="gl-m-0 gl-text-sm gl-font-bold" data-testid="reason-header">
|
|
148
|
+
{{ reasonHeader }}
|
|
149
|
+
</p>
|
|
150
|
+
<div class="gl-flex gl-shrink-0 gl-items-center gl-gap-1">
|
|
151
|
+
<gl-button
|
|
152
|
+
v-if="isTellUsMoreVisible"
|
|
153
|
+
icon="go-back"
|
|
154
|
+
category="tertiary"
|
|
155
|
+
size="small"
|
|
156
|
+
:aria-label="$options.i18n.BACK"
|
|
157
|
+
data-testid="back-button"
|
|
158
|
+
@click="goBackToReasons"
|
|
159
|
+
/>
|
|
160
|
+
<gl-button
|
|
161
|
+
icon="close"
|
|
162
|
+
category="tertiary"
|
|
163
|
+
size="small"
|
|
164
|
+
class="gl-mr-[-5px]"
|
|
165
|
+
:aria-label="$options.i18n.CLOSE"
|
|
166
|
+
data-testid="close-dropdown-button"
|
|
167
|
+
@click="close"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div
|
|
173
|
+
v-if="isReasonListVisible"
|
|
174
|
+
class="gl-flex gl-flex-wrap gl-gap-2"
|
|
175
|
+
data-testid="reason-buttons"
|
|
176
|
+
>
|
|
177
|
+
<gl-button
|
|
178
|
+
v-for="reason in reasonOptions"
|
|
179
|
+
:key="reason.key"
|
|
180
|
+
category="secondary"
|
|
181
|
+
size="small"
|
|
182
|
+
class="gl-rounded-full"
|
|
183
|
+
:icon="reason.isCustom ? 'pencil' : undefined"
|
|
184
|
+
:data-testid="`reason-${reason.key}`"
|
|
185
|
+
@click="selectReason(reason)"
|
|
186
|
+
>
|
|
187
|
+
{{ reason.label }}
|
|
188
|
+
</gl-button>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div
|
|
192
|
+
v-if="isTellUsMoreVisible"
|
|
193
|
+
class="gl-flex gl-w-full gl-flex-col gl-gap-3"
|
|
194
|
+
data-testid="tell-us-more-view"
|
|
195
|
+
>
|
|
196
|
+
<gl-form
|
|
197
|
+
class="gl-flex gl-w-full gl-flex-col gl-gap-3"
|
|
198
|
+
@submit.prevent="canSubmitCustomFeedback ? submitCustomFeedback() : null"
|
|
199
|
+
>
|
|
200
|
+
<gl-form-textarea
|
|
201
|
+
v-model="customFeedback"
|
|
202
|
+
:state="isTextareaValid"
|
|
203
|
+
:placeholder="$options.i18n.FEEDBACK_PLACEHOLDER"
|
|
204
|
+
:rows="3"
|
|
205
|
+
:max-rows="5"
|
|
206
|
+
data-testid="custom-feedback-textarea"
|
|
207
|
+
/>
|
|
208
|
+
<div class="gl-flex gl-items-center gl-justify-between">
|
|
209
|
+
<gl-button
|
|
210
|
+
variant="confirm"
|
|
211
|
+
size="small"
|
|
212
|
+
type="submit"
|
|
213
|
+
data-testid="submit-custom-feedback-button"
|
|
214
|
+
>
|
|
215
|
+
{{ $options.i18n.SUBMIT }}
|
|
216
|
+
</gl-button>
|
|
217
|
+
<span
|
|
218
|
+
:class="isOverCharacterLimit ? 'gl-text-danger' : 'gl-text-subtle'"
|
|
219
|
+
class="gl-text-sm"
|
|
220
|
+
data-testid="character-count"
|
|
221
|
+
>
|
|
222
|
+
{{ characterCountText }}
|
|
223
|
+
</span>
|
|
224
|
+
</div>
|
|
225
|
+
</gl-form>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</template>
|
|
@@ -16,6 +16,8 @@ import DocumentationSources from '../duo_chat_message_sources/duo_chat_message_s
|
|
|
16
16
|
// eslint-disable-next-line no-restricted-imports
|
|
17
17
|
import { copyToClipboard, concatUntilEmpty } from '../utils';
|
|
18
18
|
import AgenticBinaryFeedback from '../../../agentic_chat/components/agentic_binary_feedback/agentic_binary_feedback.vue';
|
|
19
|
+
import AgenticFeedbackPanel from '../../../agentic_chat/components/agentic_feedback_panel/agentic_feedback_panel.vue';
|
|
20
|
+
import MessageActionBar from '../message_action_bar/message_action_bar.vue';
|
|
19
21
|
import MessageFeedback from './message_feedback.vue';
|
|
20
22
|
import MarkdownRenderer from './markdown_renderer.vue';
|
|
21
23
|
import { CopyCodeElement } from './copy_code_element';
|
|
@@ -43,8 +45,10 @@ export default {
|
|
|
43
45
|
},
|
|
44
46
|
components: {
|
|
45
47
|
AgenticBinaryFeedback,
|
|
48
|
+
AgenticFeedbackPanel,
|
|
46
49
|
DocumentationSources,
|
|
47
50
|
DuoChatContextItemSelections,
|
|
51
|
+
MessageActionBar,
|
|
48
52
|
MessageFeedback,
|
|
49
53
|
MessageMap,
|
|
50
54
|
GlIcon,
|
|
@@ -130,6 +134,8 @@ export default {
|
|
|
130
134
|
messageChunks: [],
|
|
131
135
|
selectedContextItemsDefaultCollapsed: SELECTED_CONTEXT_ITEMS_DEFAULT_COLLAPSED,
|
|
132
136
|
copied: false,
|
|
137
|
+
feedbackChoice: null,
|
|
138
|
+
feedbackSubmitted: false,
|
|
133
139
|
};
|
|
134
140
|
},
|
|
135
141
|
computed: {
|
|
@@ -322,6 +328,16 @@ export default {
|
|
|
322
328
|
e.classList.remove(DUO_CODE_SCRIM_TOP_CLASS);
|
|
323
329
|
}
|
|
324
330
|
},
|
|
331
|
+
onFeedbackTypeSelected(type) {
|
|
332
|
+
this.feedbackChoice = type;
|
|
333
|
+
},
|
|
334
|
+
onFeedbackPanelSubmit(payload) {
|
|
335
|
+
this.feedbackSubmitted = true;
|
|
336
|
+
this.logEvent(payload);
|
|
337
|
+
},
|
|
338
|
+
onFeedbackPanelClose() {
|
|
339
|
+
this.feedbackChoice = null;
|
|
340
|
+
},
|
|
325
341
|
async copyMessage() {
|
|
326
342
|
try {
|
|
327
343
|
await copyToClipboard(this.message.content, this.$el);
|
|
@@ -403,42 +419,57 @@ export default {
|
|
|
403
419
|
|
|
404
420
|
<documentation-sources v-if="sources" :sources="sources" />
|
|
405
421
|
|
|
406
|
-
<
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
422
|
+
<template v-if="isAssistantMessage">
|
|
423
|
+
<message-action-bar>
|
|
424
|
+
<gl-animated-loader-icon v-if="isChunkAndNotCancelled" :is-on="true" />
|
|
425
|
+
<template v-if="shouldShowFeedbackLink && isBinaryFeedbackEnabled">
|
|
426
|
+
<agentic-binary-feedback
|
|
427
|
+
v-if="showBinaryFeedback"
|
|
428
|
+
:feedback-choice="feedbackChoice"
|
|
429
|
+
:submitted="feedbackSubmitted"
|
|
430
|
+
data-testid="agentic-feedback-latest"
|
|
431
|
+
@select-type="onFeedbackTypeSelected"
|
|
432
|
+
/>
|
|
433
|
+
<div
|
|
434
|
+
v-else
|
|
435
|
+
class="agentic-feedback-hover-wrapper"
|
|
436
|
+
:class="{
|
|
437
|
+
'-gl-mr-3 gl-w-0 gl-overflow-hidden gl-opacity-0':
|
|
438
|
+
!feedbackChoice && !feedbackSubmitted,
|
|
439
|
+
}"
|
|
440
|
+
data-testid="agentic-feedback-hover-container"
|
|
441
|
+
>
|
|
442
|
+
<agentic-binary-feedback
|
|
443
|
+
:feedback-choice="feedbackChoice"
|
|
444
|
+
:submitted="feedbackSubmitted"
|
|
445
|
+
@select-type="onFeedbackTypeSelected"
|
|
446
|
+
/>
|
|
447
|
+
</div>
|
|
448
|
+
</template>
|
|
449
|
+
<message-feedback
|
|
450
|
+
v-else-if="shouldShowFeedbackLink"
|
|
451
|
+
:has-feedback="hasFeedback"
|
|
415
452
|
@feedback="logEvent"
|
|
416
453
|
/>
|
|
417
|
-
<
|
|
418
|
-
v-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
:icon="copied ? 'check-circle-filled' : 'copy-to-clipboard'"
|
|
435
|
-
:class="{ '!gl-text-success': copied, '!gl-text-subtle': !copied }"
|
|
436
|
-
category="tertiary"
|
|
437
|
-
size="small"
|
|
438
|
-
@click="copyMessage"
|
|
439
|
-
@focusout="copied = false"
|
|
454
|
+
<gl-button
|
|
455
|
+
v-if="shouldShowCopyAction"
|
|
456
|
+
v-gl-tooltip
|
|
457
|
+
:title="copied ? $options.i18n.CHAT_MESSAGE_COPIED : $options.i18n.CHAT_MESSAGE_COPY"
|
|
458
|
+
:icon="copied ? 'check-circle-filled' : 'copy-to-clipboard'"
|
|
459
|
+
:class="{ '!gl-text-success': copied, '!gl-text-subtle': !copied }"
|
|
460
|
+
category="tertiary"
|
|
461
|
+
size="small"
|
|
462
|
+
@click="copyMessage"
|
|
463
|
+
@focusout="copied = false"
|
|
464
|
+
/>
|
|
465
|
+
</message-action-bar>
|
|
466
|
+
<agentic-feedback-panel
|
|
467
|
+
v-if="feedbackChoice && !feedbackSubmitted"
|
|
468
|
+
:feedback-type="feedbackChoice"
|
|
469
|
+
@submit="onFeedbackPanelSubmit"
|
|
470
|
+
@close="onFeedbackPanelClose"
|
|
440
471
|
/>
|
|
441
|
-
</
|
|
472
|
+
</template>
|
|
442
473
|
|
|
443
474
|
<duo-chat-context-item-selections
|
|
444
475
|
v-if="displaySelectedContextItems && isUserMessage"
|