@gitlab/ui 95.0.0 → 95.1.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/CHANGELOG.md +19 -0
- package/bin/migrate_custom_utils_to_tw.bundled.mjs +0 -19
- package/dist/components/experimental/duo/chat/components/duo_chat_context/constants.js +4 -1
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.js +120 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.js +11 -1
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.js +29 -2
- package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +14 -1
- package/dist/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.js +4 -1
- package/dist/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.js +7 -1
- package/dist/components/experimental/duo/chat/duo_chat.js +9 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/package.json +1 -1
- package/src/components/base/dropdown/dropdown.scss +1 -1
- package/src/components/base/dropdown/dropdown_section_header.scss +1 -1
- package/src/components/experimental/duo/chat/components/duo_chat_context/constants.js +4 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.vue +114 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.vue +10 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.vue +42 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +15 -0
- package/src/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.vue +4 -0
- package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.vue +8 -0
- package/src/components/experimental/duo/chat/duo_chat.vue +9 -0
- package/translations.js +1 -0
package/package.json
CHANGED
|
@@ -5,3 +5,7 @@ export const CONTEXT_ITEM_CATEGORY_LOCAL_GIT = 'local_git';
|
|
|
5
5
|
|
|
6
6
|
export const CONTEXT_ITEM_LOCAL_GIT_COMMIT = 'commit';
|
|
7
7
|
export const CONTEXT_ITEM_LOCAL_GIT_DIFF = 'diff';
|
|
8
|
+
|
|
9
|
+
export const LANGUAGE_IDENTIFIER_PREFIX = 'language-';
|
|
10
|
+
export const LANGUAGE_IDENTIFIER_DIFF = 'language-diff';
|
|
11
|
+
export const LANGUAGE_IDENTIFIER_PLAINTEXT = 'language-plaintext';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { nextTick } from 'vue';
|
|
3
|
+
import { contextItemValidator } from '../utils';
|
|
4
|
+
import GlModal from '../../../../../../base/modal/modal.vue';
|
|
5
|
+
import { SafeHtmlDirective as SafeHtml } from '../../../../../../../directives/safe_html/safe_html';
|
|
6
|
+
import GlSkeletonLoader from '../../../../../../base/skeleton_loader/skeleton_loader.vue';
|
|
7
|
+
import { translate } from '../../../../../../../utils/i18n';
|
|
8
|
+
import {
|
|
9
|
+
CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
|
|
10
|
+
LANGUAGE_IDENTIFIER_DIFF,
|
|
11
|
+
LANGUAGE_IDENTIFIER_PLAINTEXT,
|
|
12
|
+
LANGUAGE_IDENTIFIER_PREFIX,
|
|
13
|
+
} from '../constants';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: 'GlDuoChatContextItemDetailsModal',
|
|
17
|
+
components: {
|
|
18
|
+
GlSkeletonLoader,
|
|
19
|
+
GlModal,
|
|
20
|
+
},
|
|
21
|
+
directives: {
|
|
22
|
+
SafeHtml,
|
|
23
|
+
},
|
|
24
|
+
inject: {
|
|
25
|
+
renderGFM: {
|
|
26
|
+
from: 'renderGFM',
|
|
27
|
+
default: () => (element) => {
|
|
28
|
+
element.classList.add('gl-markdown', 'gl-compact-markdown');
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
props: {
|
|
33
|
+
/**
|
|
34
|
+
* Context items to preview. If it has no `content`, the loading state will be displayed.
|
|
35
|
+
*/
|
|
36
|
+
contextItem: {
|
|
37
|
+
type: Object,
|
|
38
|
+
required: true,
|
|
39
|
+
validator: contextItemValidator,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
computed: {
|
|
43
|
+
isLoadingContent() {
|
|
44
|
+
return this.contextItem.content === undefined;
|
|
45
|
+
},
|
|
46
|
+
languageIdentifierClass() {
|
|
47
|
+
if (this.contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
|
|
48
|
+
return LANGUAGE_IDENTIFIER_DIFF;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const fileExtension = this.contextItem.metadata?.relativePath?.split('.').at(-1);
|
|
52
|
+
if (fileExtension && fileExtension !== this.contextItem.metadata?.relativePath) {
|
|
53
|
+
return `${LANGUAGE_IDENTIFIER_PREFIX}${fileExtension}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return LANGUAGE_IDENTIFIER_PLAINTEXT;
|
|
57
|
+
},
|
|
58
|
+
title() {
|
|
59
|
+
return (
|
|
60
|
+
this.contextItem.metadata?.title ||
|
|
61
|
+
this.contextItem.metadata?.relativePath ||
|
|
62
|
+
translate('GlDuoChatContextItemDetailsModal.title', 'Preview')
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
watch: {
|
|
67
|
+
contextItem: {
|
|
68
|
+
async handler(newVal, oldVal) {
|
|
69
|
+
const shouldFormat = newVal?.content !== oldVal?.content && newVal?.content;
|
|
70
|
+
if (shouldFormat) {
|
|
71
|
+
await nextTick();
|
|
72
|
+
await this.hydrateContentWithGFM();
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
immediate: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
methods: {
|
|
79
|
+
async hydrateContentWithGFM() {
|
|
80
|
+
await nextTick();
|
|
81
|
+
|
|
82
|
+
if (this.$refs.content) {
|
|
83
|
+
this.renderGFM(this.$refs.content);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
onModalVisibilityChange(isVisible) {
|
|
87
|
+
if (!isVisible) {
|
|
88
|
+
this.$emit('close');
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<template>
|
|
96
|
+
<gl-modal
|
|
97
|
+
modal-id="context-item-details-modal"
|
|
98
|
+
:title="title"
|
|
99
|
+
:visible="true"
|
|
100
|
+
:scrollable="true"
|
|
101
|
+
hide-footer
|
|
102
|
+
size="lg"
|
|
103
|
+
@change="onModalVisibilityChange"
|
|
104
|
+
>
|
|
105
|
+
<gl-skeleton-loader v-if="isLoadingContent" />
|
|
106
|
+
<div v-else ref="content" data-testid="context-item-content">
|
|
107
|
+
<pre
|
|
108
|
+
v-safe-html="contextItem.content"
|
|
109
|
+
class="code js-syntax-highlight gl-p-3"
|
|
110
|
+
:class="languageIdentifierClass"
|
|
111
|
+
></pre>
|
|
112
|
+
</div>
|
|
113
|
+
</gl-modal>
|
|
114
|
+
</template>
|
|
@@ -218,6 +218,14 @@ export default {
|
|
|
218
218
|
|
|
219
219
|
this.activeIndex = newIndex;
|
|
220
220
|
},
|
|
221
|
+
onGetContextItemContent(contextItem) {
|
|
222
|
+
/**
|
|
223
|
+
* Emit get-context-item-content event that tells clients to load the full file content for a selected context item.
|
|
224
|
+
* The fully hydrated context item should be updated in the context item selections.
|
|
225
|
+
* @param {*} event An event containing the context item to hydrate
|
|
226
|
+
*/
|
|
227
|
+
this.$emit('get-context-item-content', { contextItem });
|
|
228
|
+
},
|
|
221
229
|
},
|
|
222
230
|
i18n: {
|
|
223
231
|
selectedContextItemsTitle: translate(
|
|
@@ -233,11 +241,13 @@ export default {
|
|
|
233
241
|
<gl-duo-chat-context-item-selections
|
|
234
242
|
v-if="selections.length"
|
|
235
243
|
:selections="selections"
|
|
244
|
+
:categories="categories"
|
|
236
245
|
:removable="true"
|
|
237
246
|
:title="$options.i18n.selectedContextItemsTitle"
|
|
238
247
|
:default-collapsed="false"
|
|
239
248
|
class="gl-mb-3"
|
|
240
249
|
@remove="removeItem"
|
|
250
|
+
@get-content="onGetContextItemContent"
|
|
241
251
|
/>
|
|
242
252
|
<gl-card
|
|
243
253
|
v-if="open"
|
|
@@ -4,12 +4,15 @@ import GlIcon from '../../../../../../base/icon/icon.vue';
|
|
|
4
4
|
import GlToken from '../../../../../../base/token/token.vue';
|
|
5
5
|
import GlTruncate from '../../../../../../utilities/truncate/truncate.vue';
|
|
6
6
|
import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover.vue';
|
|
7
|
+
import { CONTEXT_ITEM_CATEGORY_FILE, CONTEXT_ITEM_CATEGORY_LOCAL_GIT } from '../constants';
|
|
8
|
+
import GlDuoChatContextItemDetailsModal from '../duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.vue';
|
|
7
9
|
import { contextItemsValidator, getContextItemIcon } from '../utils';
|
|
8
10
|
|
|
9
11
|
export default {
|
|
10
12
|
name: 'GlDuoChatContextItemSelections',
|
|
11
13
|
components: {
|
|
12
14
|
GlTruncate,
|
|
15
|
+
GlDuoChatContextItemDetailsModal,
|
|
13
16
|
GlIcon,
|
|
14
17
|
GlDuoChatContextItemPopover,
|
|
15
18
|
GlToken,
|
|
@@ -55,6 +58,7 @@ export default {
|
|
|
55
58
|
return {
|
|
56
59
|
isCollapsed: this.defaultCollapsed,
|
|
57
60
|
selectionsId: uniqueId(),
|
|
61
|
+
previewContextItemId: null,
|
|
58
62
|
};
|
|
59
63
|
},
|
|
60
64
|
computed: {
|
|
@@ -73,6 +77,9 @@ export default {
|
|
|
73
77
|
}
|
|
74
78
|
return '';
|
|
75
79
|
},
|
|
80
|
+
contextItemPreview() {
|
|
81
|
+
return this.selections.find((item) => item.id === this.previewContextItemId);
|
|
82
|
+
},
|
|
76
83
|
},
|
|
77
84
|
methods: {
|
|
78
85
|
getContextItemIcon,
|
|
@@ -86,6 +93,30 @@ export default {
|
|
|
86
93
|
*/
|
|
87
94
|
this.$emit('remove', contextItem);
|
|
88
95
|
},
|
|
96
|
+
onOpenItem(event, contextItem) {
|
|
97
|
+
const isKeypressOnCloseButton =
|
|
98
|
+
event.type === 'keydown' && event.target !== event.currentTarget;
|
|
99
|
+
if (isKeypressOnCloseButton) {
|
|
100
|
+
// don't respond to events triggered by the gl-token children (e.g. the close button)
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!this.canOpen(contextItem)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!contextItem.content) {
|
|
108
|
+
this.$emit('get-content', contextItem);
|
|
109
|
+
}
|
|
110
|
+
this.previewContextItemId = contextItem.id;
|
|
111
|
+
},
|
|
112
|
+
canOpen(contextItem) {
|
|
113
|
+
return [CONTEXT_ITEM_CATEGORY_LOCAL_GIT, CONTEXT_ITEM_CATEGORY_FILE].includes(
|
|
114
|
+
contextItem.category
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
onClosePreview() {
|
|
118
|
+
this.previewContextItemId = null;
|
|
119
|
+
},
|
|
89
120
|
},
|
|
90
121
|
};
|
|
91
122
|
</script>
|
|
@@ -114,6 +145,11 @@ export default {
|
|
|
114
145
|
variant="default"
|
|
115
146
|
class="gl-mb-2 gl-mr-2 gl-max-w-full"
|
|
116
147
|
:class="tokenVariantClasses"
|
|
148
|
+
:tabindex="canOpen(item) ? 0 : -1"
|
|
149
|
+
:role="canOpen(item) ? 'button' : undefined"
|
|
150
|
+
@click="onOpenItem($event, item)"
|
|
151
|
+
@keydown.enter="onOpenItem($event, item)"
|
|
152
|
+
@keydown.space.prevent="onOpenItem($event, item)"
|
|
117
153
|
@close="onRemoveItem(item)"
|
|
118
154
|
>
|
|
119
155
|
<div
|
|
@@ -132,8 +168,14 @@ export default {
|
|
|
132
168
|
:context-item="item"
|
|
133
169
|
:target="`context-item-${item.id}-${selectionsId}-token`"
|
|
134
170
|
placement="bottom"
|
|
171
|
+
@show-git-diff="onOpenItem(item)"
|
|
135
172
|
/>
|
|
136
173
|
</gl-token>
|
|
137
174
|
</div>
|
|
175
|
+
<gl-duo-chat-context-item-details-modal
|
|
176
|
+
v-if="contextItemPreview"
|
|
177
|
+
:context-item="contextItemPreview"
|
|
178
|
+
@close="onClosePreview"
|
|
179
|
+
/>
|
|
138
180
|
</div>
|
|
139
181
|
</template>
|
package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js
CHANGED
|
@@ -16,6 +16,21 @@ export function getMockCategory(categoryValue) {
|
|
|
16
16
|
return MOCK_CATEGORIES.find((cat) => cat.value === categoryValue);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export const MOCK_CONTEXT_FILE_CONTENT = `export function waterPlants() {
|
|
20
|
+
console.log('sprinkle');
|
|
21
|
+
}`;
|
|
22
|
+
|
|
23
|
+
export const MOCK_CONTEXT_FILE_DIFF_CONTENT = `diff --git a/src/plants/strawberry.ts b/src/plants/strawberry.ts
|
|
24
|
+
index 1234567..8901234 100644
|
|
25
|
+
--- a/src/plants/strawberry.ts
|
|
26
|
+
+++ b/src/plants/strawberry.ts
|
|
27
|
+
@@ -1,4 +1,4 @@
|
|
28
|
+
export const strawberry = {
|
|
29
|
+
name: 'Strawberry',
|
|
30
|
+
- waterNeeds: 'moderate',
|
|
31
|
+
+ waterNeeds: 'high',
|
|
32
|
+
};`;
|
|
33
|
+
|
|
19
34
|
export const MOCK_CONTEXT_ITEM_FILE = {
|
|
20
35
|
id: '123e4567-e89b-12d3-a456-426614174000',
|
|
21
36
|
category: CONTEXT_ITEM_CATEGORY_FILE,
|
|
@@ -57,6 +57,9 @@ export default {
|
|
|
57
57
|
onInsertCodeSnippet(e) {
|
|
58
58
|
this.$emit('insert-code-snippet', e);
|
|
59
59
|
},
|
|
60
|
+
onGetContextItemContent(e) {
|
|
61
|
+
this.$emit('get-context-item-content', e);
|
|
62
|
+
},
|
|
60
63
|
},
|
|
61
64
|
i18n,
|
|
62
65
|
};
|
|
@@ -81,6 +84,7 @@ export default {
|
|
|
81
84
|
:is-cancelled="canceledRequestIds.includes(msg.requestId)"
|
|
82
85
|
@track-feedback="onTrackFeedback"
|
|
83
86
|
@insert-code-snippet="onInsertCodeSnippet"
|
|
87
|
+
@get-context-item-content="onGetContextItemContent"
|
|
84
88
|
/>
|
|
85
89
|
</div>
|
|
86
90
|
</template>
|
package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.vue
CHANGED
|
@@ -234,6 +234,12 @@ export default {
|
|
|
234
234
|
onInsertCodeSnippet(e) {
|
|
235
235
|
this.$emit('insert-code-snippet', e);
|
|
236
236
|
},
|
|
237
|
+
onGetContextItemContent(contextItem) {
|
|
238
|
+
this.$emit('get-context-item-content', {
|
|
239
|
+
messageId: this.message.id,
|
|
240
|
+
contextItem,
|
|
241
|
+
});
|
|
242
|
+
},
|
|
237
243
|
},
|
|
238
244
|
};
|
|
239
245
|
</script>
|
|
@@ -264,6 +270,7 @@ export default {
|
|
|
264
270
|
:title="selectedContextItemsTitle"
|
|
265
271
|
:default-collapsed="selectedContextItemsDefaultCollapsed"
|
|
266
272
|
variant="assistant"
|
|
273
|
+
@get-content="onGetContextItemContent"
|
|
267
274
|
/>
|
|
268
275
|
<div
|
|
269
276
|
v-if="error"
|
|
@@ -309,6 +316,7 @@ export default {
|
|
|
309
316
|
:title="selectedContextItemsTitle"
|
|
310
317
|
:default-collapsed="selectedContextItemsDefaultCollapsed"
|
|
311
318
|
variant="user"
|
|
319
|
+
@get-content="onGetContextItemContent"
|
|
312
320
|
/>
|
|
313
321
|
</div>
|
|
314
322
|
</div>
|
|
@@ -518,6 +518,14 @@ export default {
|
|
|
518
518
|
*/
|
|
519
519
|
this.$emit('insert-code-snippet', e);
|
|
520
520
|
},
|
|
521
|
+
onGetContextItemContent(event) {
|
|
522
|
+
/**
|
|
523
|
+
* Emit get-context-item-content event that tells clients to load the full file content for a selected context item.
|
|
524
|
+
* The fully hydrated context item should be updated in the chat message context item.
|
|
525
|
+
* @param {*} event An event containing the message ID and context item to hydrate
|
|
526
|
+
*/
|
|
527
|
+
this.$emit('get-context-item-content', event);
|
|
528
|
+
},
|
|
521
529
|
closeContextItemsMenuOpen() {
|
|
522
530
|
this.contextItemsMenuIsOpen = false;
|
|
523
531
|
this.setPromptAndFocus();
|
|
@@ -603,6 +611,7 @@ export default {
|
|
|
603
611
|
:show-delimiter="index > 0"
|
|
604
612
|
@track-feedback="onTrackFeedback"
|
|
605
613
|
@insert-code-snippet="onInsertCodeSnippet"
|
|
614
|
+
@get-context-item-content="onGetContextItemContent"
|
|
606
615
|
/>
|
|
607
616
|
<template v-if="!hasMessages && !isLoading">
|
|
608
617
|
<gl-empty-state
|
package/translations.js
CHANGED
|
@@ -27,6 +27,7 @@ export default {
|
|
|
27
27
|
'GlDuoChat.chatPromptPlaceholderDefault': 'GitLab Duo Chat',
|
|
28
28
|
'GlDuoChat.chatPromptPlaceholderWithCommands': 'Type "/" for slash commands',
|
|
29
29
|
'GlDuoChat.chatSubmitLabel': 'Send chat message.',
|
|
30
|
+
'GlDuoChatContextItemDetailsModal.title': 'Preview',
|
|
30
31
|
'GlDuoChatContextItemMenu.emptyStateMessage': 'No results found',
|
|
31
32
|
'GlDuoChatContextItemMenu.loadingMessage': 'Loading...',
|
|
32
33
|
'GlDuoChatContextItemMenu.searchInputPlaceholder': 'Search %{categoryLabel}...',
|