@gitlab/ui 95.0.0 → 95.1.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.
Files changed (19) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/experimental/duo/chat/components/duo_chat_context/constants.js +4 -1
  3. 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
  4. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.js +11 -1
  5. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.js +29 -2
  6. package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +14 -1
  7. package/dist/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.js +4 -1
  8. package/dist/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.js +7 -1
  9. package/dist/components/experimental/duo/chat/duo_chat.js +9 -1
  10. package/package.json +1 -1
  11. package/src/components/experimental/duo/chat/components/duo_chat_context/constants.js +4 -0
  12. 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
  13. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.vue +10 -0
  14. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.vue +42 -0
  15. package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +15 -0
  16. package/src/components/experimental/duo/chat/components/duo_chat_conversation/duo_chat_conversation.vue +4 -0
  17. package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.vue +8 -0
  18. package/src/components/experimental/duo/chat/duo_chat.vue +9 -0
  19. package/translations.js +1 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [95.1.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v95.0.0...v95.1.0) (2024-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * don't open details modal if selecting close button via keyboard ([8e8a4c3](https://gitlab.com/gitlab-org/gitlab-ui/commit/8e8a4c3dc7fc9f30d4c02a3a9a4acf7da96c2aac))
7
+
8
+
9
+ ### Features
10
+
11
+ * **GlDuoChat:** view context item content/details ([63909dd](https://gitlab.com/gitlab-org/gitlab-ui/commit/63909dd79aa2570ecd7375c22619d892ea1935c2))
12
+
1
13
  # [95.0.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v94.10.0...v95.0.0) (2024-10-04)
2
14
 
3
15
 
@@ -4,5 +4,8 @@ const CONTEXT_ITEM_CATEGORY_FILE = 'file';
4
4
  const CONTEXT_ITEM_CATEGORY_LOCAL_GIT = 'local_git';
5
5
  const CONTEXT_ITEM_LOCAL_GIT_COMMIT = 'commit';
6
6
  const CONTEXT_ITEM_LOCAL_GIT_DIFF = 'diff';
7
+ const LANGUAGE_IDENTIFIER_PREFIX = 'language-';
8
+ const LANGUAGE_IDENTIFIER_DIFF = 'language-diff';
9
+ const LANGUAGE_IDENTIFIER_PLAINTEXT = 'language-plaintext';
7
10
 
8
- export { CONTEXT_ITEM_CATEGORY_FILE, CONTEXT_ITEM_CATEGORY_ISSUE, CONTEXT_ITEM_CATEGORY_LOCAL_GIT, CONTEXT_ITEM_CATEGORY_MERGE_REQUEST, CONTEXT_ITEM_LOCAL_GIT_COMMIT, CONTEXT_ITEM_LOCAL_GIT_DIFF };
11
+ export { CONTEXT_ITEM_CATEGORY_FILE, CONTEXT_ITEM_CATEGORY_ISSUE, CONTEXT_ITEM_CATEGORY_LOCAL_GIT, CONTEXT_ITEM_CATEGORY_MERGE_REQUEST, CONTEXT_ITEM_LOCAL_GIT_COMMIT, CONTEXT_ITEM_LOCAL_GIT_DIFF, LANGUAGE_IDENTIFIER_DIFF, LANGUAGE_IDENTIFIER_PLAINTEXT, LANGUAGE_IDENTIFIER_PREFIX };
@@ -0,0 +1,120 @@
1
+ import { nextTick } from 'vue';
2
+ import { contextItemValidator } from '../utils';
3
+ import GlModal from '../../../../../../base/modal/modal';
4
+ import { SafeHtmlDirective } from '../../../../../../../directives/safe_html/safe_html';
5
+ import GlSkeletonLoader from '../../../../../../base/skeleton_loader/skeleton_loader';
6
+ import { translate } from '../../../../../../../utils/i18n';
7
+ import { CONTEXT_ITEM_CATEGORY_LOCAL_GIT, LANGUAGE_IDENTIFIER_DIFF, LANGUAGE_IDENTIFIER_PREFIX, LANGUAGE_IDENTIFIER_PLAINTEXT } from '../constants';
8
+ import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
9
+
10
+ var script = {
11
+ name: 'GlDuoChatContextItemDetailsModal',
12
+ components: {
13
+ GlSkeletonLoader,
14
+ GlModal
15
+ },
16
+ directives: {
17
+ SafeHtml: SafeHtmlDirective
18
+ },
19
+ inject: {
20
+ renderGFM: {
21
+ from: 'renderGFM',
22
+ default: () => element => {
23
+ element.classList.add('gl-markdown', 'gl-compact-markdown');
24
+ }
25
+ }
26
+ },
27
+ props: {
28
+ /**
29
+ * Context items to preview. If it has no `content`, the loading state will be displayed.
30
+ */
31
+ contextItem: {
32
+ type: Object,
33
+ required: true,
34
+ validator: contextItemValidator
35
+ }
36
+ },
37
+ computed: {
38
+ isLoadingContent() {
39
+ return this.contextItem.content === undefined;
40
+ },
41
+ languageIdentifierClass() {
42
+ var _this$contextItem$met, _this$contextItem$met2, _this$contextItem$met3;
43
+ if (this.contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
44
+ return LANGUAGE_IDENTIFIER_DIFF;
45
+ }
46
+ const fileExtension = (_this$contextItem$met = this.contextItem.metadata) === null || _this$contextItem$met === void 0 ? void 0 : (_this$contextItem$met2 = _this$contextItem$met.relativePath) === null || _this$contextItem$met2 === void 0 ? void 0 : _this$contextItem$met2.split('.').at(-1);
47
+ if (fileExtension && fileExtension !== ((_this$contextItem$met3 = this.contextItem.metadata) === null || _this$contextItem$met3 === void 0 ? void 0 : _this$contextItem$met3.relativePath)) {
48
+ return `${LANGUAGE_IDENTIFIER_PREFIX}${fileExtension}`;
49
+ }
50
+ return LANGUAGE_IDENTIFIER_PLAINTEXT;
51
+ },
52
+ title() {
53
+ var _this$contextItem$met4, _this$contextItem$met5;
54
+ return ((_this$contextItem$met4 = this.contextItem.metadata) === null || _this$contextItem$met4 === void 0 ? void 0 : _this$contextItem$met4.title) || ((_this$contextItem$met5 = this.contextItem.metadata) === null || _this$contextItem$met5 === void 0 ? void 0 : _this$contextItem$met5.relativePath) || translate('GlDuoChatContextItemDetailsModal.title', 'Preview');
55
+ }
56
+ },
57
+ watch: {
58
+ contextItem: {
59
+ async handler(newVal, oldVal) {
60
+ const shouldFormat = (newVal === null || newVal === void 0 ? void 0 : newVal.content) !== (oldVal === null || oldVal === void 0 ? void 0 : oldVal.content) && (newVal === null || newVal === void 0 ? void 0 : newVal.content);
61
+ if (shouldFormat) {
62
+ await nextTick();
63
+ await this.hydrateContentWithGFM();
64
+ }
65
+ },
66
+ immediate: true
67
+ }
68
+ },
69
+ methods: {
70
+ async hydrateContentWithGFM() {
71
+ await nextTick();
72
+ if (this.$refs.content) {
73
+ this.renderGFM(this.$refs.content);
74
+ }
75
+ },
76
+ onModalVisibilityChange(isVisible) {
77
+ if (!isVisible) {
78
+ this.$emit('close');
79
+ }
80
+ }
81
+ }
82
+ };
83
+
84
+ /* script */
85
+ const __vue_script__ = script;
86
+
87
+ /* template */
88
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-modal',{attrs:{"modal-id":"context-item-details-modal","title":_vm.title,"visible":true,"scrollable":true,"hide-footer":"","size":"lg"},on:{"change":_vm.onModalVisibilityChange}},[(_vm.isLoadingContent)?_c('gl-skeleton-loader'):_c('div',{ref:"content",attrs:{"data-testid":"context-item-content"}},[_c('pre',{directives:[{name:"safe-html",rawName:"v-safe-html",value:(_vm.contextItem.content),expression:"contextItem.content"}],staticClass:"code js-syntax-highlight gl-p-3",class:_vm.languageIdentifierClass})])],1)};
89
+ var __vue_staticRenderFns__ = [];
90
+
91
+ /* style */
92
+ const __vue_inject_styles__ = undefined;
93
+ /* scoped */
94
+ const __vue_scope_id__ = undefined;
95
+ /* module identifier */
96
+ const __vue_module_identifier__ = undefined;
97
+ /* functional template */
98
+ const __vue_is_functional_template__ = false;
99
+ /* style inject */
100
+
101
+ /* style inject SSR */
102
+
103
+ /* style inject shadow dom */
104
+
105
+
106
+
107
+ const __vue_component__ = __vue_normalize__(
108
+ { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
109
+ __vue_inject_styles__,
110
+ __vue_script__,
111
+ __vue_scope_id__,
112
+ __vue_is_functional_template__,
113
+ __vue_module_identifier__,
114
+ false,
115
+ undefined,
116
+ undefined,
117
+ undefined
118
+ );
119
+
120
+ export default __vue_component__;
@@ -209,6 +209,16 @@ var script = {
209
209
  }
210
210
  } while (!this.results[newIndex].metadata.enabled);
211
211
  this.activeIndex = newIndex;
212
+ },
213
+ onGetContextItemContent(contextItem) {
214
+ /**
215
+ * Emit get-context-item-content event that tells clients to load the full file content for a selected context item.
216
+ * The fully hydrated context item should be updated in the context item selections.
217
+ * @param {*} event An event containing the context item to hydrate
218
+ */
219
+ this.$emit('get-context-item-content', {
220
+ contextItem
221
+ });
212
222
  }
213
223
  },
214
224
  i18n: {
@@ -220,7 +230,7 @@ var script = {
220
230
  const __vue_script__ = script;
221
231
 
222
232
  /* template */
223
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.selections.length)?_c('gl-duo-chat-context-item-selections',{staticClass:"gl-mb-3",attrs:{"selections":_vm.selections,"removable":true,"title":_vm.$options.i18n.selectedContextItemsTitle,"default-collapsed":false},on:{"remove":_vm.removeItem}}):_vm._e(),_vm._v(" "),(_vm.open)?_c('gl-card',{staticClass:"slash-commands !gl-absolute gl-bottom-0 gl-w-full gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2","data-testid":"context-item-menu"}},[(_vm.showCategorySelection)?_c('gl-duo-chat-context-item-menu-category-items',{attrs:{"active-index":_vm.activeIndex,"categories":_vm.categories},on:{"select":_vm.selectCategory,"active-index-change":function($event){_vm.activeIndex = $event;}}}):_c('gl-duo-chat-context-item-menu-search-items',{attrs:{"active-index":_vm.activeIndex,"category":_vm.selectedCategory,"loading":_vm.loading,"error":_vm.error,"results":_vm.results},on:{"select":_vm.selectItem,"keyup":_vm.handleKeyUp,"active-index-change":function($event){_vm.activeIndex = $event;}},model:{value:(_vm.searchQuery),callback:function ($$v) {_vm.searchQuery=$$v;},expression:"searchQuery"}})],1):_vm._e()],1)};
233
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.selections.length)?_c('gl-duo-chat-context-item-selections',{staticClass:"gl-mb-3",attrs:{"selections":_vm.selections,"categories":_vm.categories,"removable":true,"title":_vm.$options.i18n.selectedContextItemsTitle,"default-collapsed":false},on:{"remove":_vm.removeItem,"get-content":_vm.onGetContextItemContent}}):_vm._e(),_vm._v(" "),(_vm.open)?_c('gl-card',{staticClass:"slash-commands !gl-absolute gl-bottom-0 gl-w-full gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2","data-testid":"context-item-menu"}},[(_vm.showCategorySelection)?_c('gl-duo-chat-context-item-menu-category-items',{attrs:{"active-index":_vm.activeIndex,"categories":_vm.categories},on:{"select":_vm.selectCategory,"active-index-change":function($event){_vm.activeIndex = $event;}}}):_c('gl-duo-chat-context-item-menu-search-items',{attrs:{"active-index":_vm.activeIndex,"category":_vm.selectedCategory,"loading":_vm.loading,"error":_vm.error,"results":_vm.results},on:{"select":_vm.selectItem,"keyup":_vm.handleKeyUp,"active-index-change":function($event){_vm.activeIndex = $event;}},model:{value:(_vm.searchQuery),callback:function ($$v) {_vm.searchQuery=$$v;},expression:"searchQuery"}})],1):_vm._e()],1)};
224
234
  var __vue_staticRenderFns__ = [];
225
235
 
226
236
  /* style */
@@ -3,6 +3,8 @@ import GlIcon from '../../../../../../base/icon/icon';
3
3
  import GlToken from '../../../../../../base/token/token';
4
4
  import GlTruncate from '../../../../../../utilities/truncate/truncate';
5
5
  import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover';
6
+ import { CONTEXT_ITEM_CATEGORY_LOCAL_GIT, CONTEXT_ITEM_CATEGORY_FILE } from '../constants';
7
+ import GlDuoChatContextItemDetailsModal from '../duo_chat_context_item_details_modal/duo_chat_context_item_details_modal';
6
8
  import { contextItemsValidator, getContextItemIcon } from '../utils';
7
9
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
8
10
 
@@ -10,6 +12,7 @@ var script = {
10
12
  name: 'GlDuoChatContextItemSelections',
11
13
  components: {
12
14
  GlTruncate,
15
+ GlDuoChatContextItemDetailsModal,
13
16
  GlIcon,
14
17
  GlDuoChatContextItemPopover,
15
18
  GlToken
@@ -54,7 +57,8 @@ var script = {
54
57
  data() {
55
58
  return {
56
59
  isCollapsed: this.defaultCollapsed,
57
- selectionsId: uniqueId()
60
+ selectionsId: uniqueId(),
61
+ previewContextItemId: null
58
62
  };
59
63
  },
60
64
  computed: {
@@ -72,6 +76,9 @@ var script = {
72
76
  return 'gl-bg-blue-50 gl-text-blue-600';
73
77
  }
74
78
  return '';
79
+ },
80
+ contextItemPreview() {
81
+ return this.selections.find(item => item.id === this.previewContextItemId);
75
82
  }
76
83
  },
77
84
  methods: {
@@ -85,6 +92,26 @@ var script = {
85
92
  * @property {Object} item - The context contextItem to be removed
86
93
  */
87
94
  this.$emit('remove', contextItem);
95
+ },
96
+ onOpenItem(event, contextItem) {
97
+ const isKeypressOnCloseButton = event.type === 'keydown' && event.target !== event.currentTarget;
98
+ if (isKeypressOnCloseButton) {
99
+ // don't respond to events triggered by the gl-token children (e.g. the close button)
100
+ return;
101
+ }
102
+ if (!this.canOpen(contextItem)) {
103
+ return;
104
+ }
105
+ if (!contextItem.content) {
106
+ this.$emit('get-content', contextItem);
107
+ }
108
+ this.previewContextItemId = contextItem.id;
109
+ },
110
+ canOpen(contextItem) {
111
+ return [CONTEXT_ITEM_CATEGORY_LOCAL_GIT, CONTEXT_ITEM_CATEGORY_FILE].includes(contextItem.category);
112
+ },
113
+ onClosePreview() {
114
+ this.previewContextItemId = null;
88
115
  }
89
116
  }
90
117
  };
@@ -93,7 +120,7 @@ var script = {
93
120
  const __vue_script__ = script;
94
121
 
95
122
  /* template */
96
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-flex gl-flex-col",class:_vm.variantClasses},[_c('button',{staticClass:"gl-flex gl-w-full gl-items-center gl-border-0 gl-bg-transparent gl-p-0 gl-text-left gl-text-xs gl-lowercase gl-text-inherit",attrs:{"data-testid":"chat-context-selections-title","type":"button"},on:{"click":_vm.toggleCollapse}},[_c('gl-icon',{attrs:{"name":_vm.collapseIconName,"data-testid":"chat-context-collapse-icon"}}),_vm._v(" "+_vm._s(_vm.title)+"\n ")],1),_vm._v(" "),_c('div',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isCollapsed),expression:"!isCollapsed"}],staticClass:"gl-mt-1 gl-flex gl-grow gl-flex-wrap",attrs:{"data-testid":"chat-context-tokens-wrapper"}},_vm._l((_vm.selections),function(item){return _c('gl-token',{key:item.id,staticClass:"gl-mb-2 gl-mr-2 gl-max-w-full",class:_vm.tokenVariantClasses,attrs:{"id":("context-item-" + (item.id) + "-" + _vm.selectionsId + "-token"),"view-only":!_vm.removable,"variant":"default"},on:{"close":function($event){return _vm.onRemoveItem(item)}}},[_c('div',{staticClass:"gl-flex gl-min-w-0 gl-items-center",attrs:{"id":("context-item-" + (item.id) + "-" + _vm.selectionsId)}},[(_vm.getContextItemIcon(item))?_c('gl-icon',{staticClass:"gl-mr-1",attrs:{"name":_vm.getContextItemIcon(item),"size":12}}):_vm._e(),_vm._v(" "),_c('gl-truncate',{attrs:{"text":item.metadata.title,"position":"middle"}})],1),_vm._v(" "),_c('gl-duo-chat-context-item-popover',{attrs:{"context-item":item,"target":("context-item-" + (item.id) + "-" + _vm.selectionsId + "-token"),"placement":"bottom"}})],1)}),1)])};
123
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-flex gl-flex-col",class:_vm.variantClasses},[_c('button',{staticClass:"gl-flex gl-w-full gl-items-center gl-border-0 gl-bg-transparent gl-p-0 gl-text-left gl-text-xs gl-lowercase gl-text-inherit",attrs:{"data-testid":"chat-context-selections-title","type":"button"},on:{"click":_vm.toggleCollapse}},[_c('gl-icon',{attrs:{"name":_vm.collapseIconName,"data-testid":"chat-context-collapse-icon"}}),_vm._v(" "+_vm._s(_vm.title)+"\n ")],1),_vm._v(" "),_c('div',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isCollapsed),expression:"!isCollapsed"}],staticClass:"gl-mt-1 gl-flex gl-grow gl-flex-wrap",attrs:{"data-testid":"chat-context-tokens-wrapper"}},_vm._l((_vm.selections),function(item){return _c('gl-token',{key:item.id,staticClass:"gl-mb-2 gl-mr-2 gl-max-w-full",class:_vm.tokenVariantClasses,attrs:{"id":("context-item-" + (item.id) + "-" + _vm.selectionsId + "-token"),"view-only":!_vm.removable,"variant":"default","tabindex":_vm.canOpen(item) ? 0 : -1,"role":_vm.canOpen(item) ? 'button' : undefined},on:{"click":function($event){return _vm.onOpenItem($event, item)},"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }return _vm.onOpenItem($event, item)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"space",32,$event.key,[" ","Spacebar"])){ return null; }$event.preventDefault();return _vm.onOpenItem($event, item)}],"close":function($event){return _vm.onRemoveItem(item)}}},[_c('div',{staticClass:"gl-flex gl-min-w-0 gl-items-center",attrs:{"id":("context-item-" + (item.id) + "-" + _vm.selectionsId)}},[(_vm.getContextItemIcon(item))?_c('gl-icon',{staticClass:"gl-mr-1",attrs:{"name":_vm.getContextItemIcon(item),"size":12}}):_vm._e(),_vm._v(" "),_c('gl-truncate',{attrs:{"text":item.metadata.title,"position":"middle"}})],1),_vm._v(" "),_c('gl-duo-chat-context-item-popover',{attrs:{"context-item":item,"target":("context-item-" + (item.id) + "-" + _vm.selectionsId + "-token"),"placement":"bottom"},on:{"show-git-diff":function($event){return _vm.onOpenItem(item)}}})],1)}),1),_vm._v(" "),(_vm.contextItemPreview)?_c('gl-duo-chat-context-item-details-modal',{attrs:{"context-item":_vm.contextItemPreview},on:{"close":_vm.onClosePreview}}):_vm._e()],1)};
97
124
  var __vue_staticRenderFns__ = [];
98
125
 
99
126
  /* style */
@@ -20,6 +20,19 @@ const MOCK_CATEGORIES = [{
20
20
  function getMockCategory(categoryValue) {
21
21
  return MOCK_CATEGORIES.find(cat => cat.value === categoryValue);
22
22
  }
23
+ const MOCK_CONTEXT_FILE_CONTENT = `export function waterPlants() {
24
+ console.log('sprinkle');
25
+ }`;
26
+ const MOCK_CONTEXT_FILE_DIFF_CONTENT = `diff --git a/src/plants/strawberry.ts b/src/plants/strawberry.ts
27
+ index 1234567..8901234 100644
28
+ --- a/src/plants/strawberry.ts
29
+ +++ b/src/plants/strawberry.ts
30
+ @@ -1,4 +1,4 @@
31
+ export const strawberry = {
32
+ name: 'Strawberry',
33
+ - waterNeeds: 'moderate',
34
+ + waterNeeds: 'high',
35
+ };`;
23
36
  const MOCK_CONTEXT_ITEM_FILE = {
24
37
  id: '123e4567-e89b-12d3-a456-426614174000',
25
38
  category: CONTEXT_ITEM_CATEGORY_FILE,
@@ -175,4 +188,4 @@ const getMockContextItems = () => {
175
188
  return [...enabledItems, ...disabledItems];
176
189
  };
177
190
 
178
- export { MOCK_CATEGORIES, MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE_DISABLED, MOCK_CONTEXT_ITEM_GIT_COMMIT, MOCK_CONTEXT_ITEM_GIT_DIFF, MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE_DISABLED, MOCK_CONTEXT_ITEM_MERGE_REQUEST, MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED, getMockCategory, getMockContextItems };
191
+ export { MOCK_CATEGORIES, MOCK_CONTEXT_FILE_CONTENT, MOCK_CONTEXT_FILE_DIFF_CONTENT, MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE_DISABLED, MOCK_CONTEXT_ITEM_GIT_COMMIT, MOCK_CONTEXT_ITEM_GIT_DIFF, MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE_DISABLED, MOCK_CONTEXT_ITEM_MERGE_REQUEST, MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED, getMockCategory, getMockContextItems };
@@ -54,6 +54,9 @@ var script = {
54
54
  },
55
55
  onInsertCodeSnippet(e) {
56
56
  this.$emit('insert-code-snippet', e);
57
+ },
58
+ onGetContextItemContent(e) {
59
+ this.$emit('get-context-item-content', e);
57
60
  }
58
61
  },
59
62
  i18n
@@ -63,7 +66,7 @@ var script = {
63
66
  const __vue_script__ = script;
64
67
 
65
68
  /* template */
66
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:['gl-flex gl-flex-col gl-justify-end', { 'insert-code-hidden': !_vm.enableCodeInsertion }]},[(_vm.showDelimiter)?_c('div',{staticClass:"gl-my-5 gl-flex gl-items-center gl-gap-4 gl-text-gray-500",attrs:{"data-testid":"conversation-delimiter"}},[_c('hr',{staticClass:"gl-grow"}),_vm._v(" "),_c('span',[_vm._v(_vm._s(_vm.$options.i18n.CONVERSATION_NEW_CHAT))]),_vm._v(" "),_c('hr',{staticClass:"gl-grow"})]):_vm._e(),_vm._v(" "),_vm._l((_vm.messages),function(msg,index){return _c('gl-duo-chat-message',{key:((msg.role) + "-" + index),attrs:{"message":msg,"is-cancelled":_vm.canceledRequestIds.includes(msg.requestId)},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet}})})],2)};
69
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:['gl-flex gl-flex-col gl-justify-end', { 'insert-code-hidden': !_vm.enableCodeInsertion }]},[(_vm.showDelimiter)?_c('div',{staticClass:"gl-my-5 gl-flex gl-items-center gl-gap-4 gl-text-gray-500",attrs:{"data-testid":"conversation-delimiter"}},[_c('hr',{staticClass:"gl-grow"}),_vm._v(" "),_c('span',[_vm._v(_vm._s(_vm.$options.i18n.CONVERSATION_NEW_CHAT))]),_vm._v(" "),_c('hr',{staticClass:"gl-grow"})]):_vm._e(),_vm._v(" "),_vm._l((_vm.messages),function(msg,index){return _c('gl-duo-chat-message',{key:((msg.role) + "-" + index),attrs:{"message":msg,"is-cancelled":_vm.canceledRequestIds.includes(msg.requestId)},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"get-context-item-content":_vm.onGetContextItemContent}})})],2)};
67
70
  var __vue_staticRenderFns__ = [];
68
71
 
69
72
  /* style */
@@ -212,6 +212,12 @@ var script = {
212
212
  },
213
213
  onInsertCodeSnippet(e) {
214
214
  this.$emit('insert-code-snippet', e);
215
+ },
216
+ onGetContextItemContent(contextItem) {
217
+ this.$emit('get-context-item-content', {
218
+ messageId: this.message.id,
219
+ contextItem
220
+ });
215
221
  }
216
222
  }
217
223
  };
@@ -226,7 +232,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
226
232
  _vm.isAssistantMessage,
227
233
  'gl-bg-white': _vm.isAssistantMessage && !_vm.error,
228
234
  '!gl-border-none gl-bg-red-50': _vm.error,
229
- },on:{"insert-code-snippet":_vm.onInsertCodeSnippet}},[(_vm.error)?_c('gl-icon',{staticClass:"error-icon gl-border gl-mr-3 gl-shrink-0 gl-rounded-full gl-border-red-500 gl-text-red-600",attrs:{"aria-label":_vm.$options.i18n.MESSAGE_ERROR,"name":"status_warning_borderless","size":16,"data-testid":"error"}}):_vm._e(),_vm._v(" "),_c('div',{ref:"content-wrapper",class:{ 'has-error': _vm.error }},[(_vm.displaySelectedContextItems && _vm.isAssistantMessage)?_c('gl-duo-chat-context-item-selections',{attrs:{"selections":_vm.selectedContextItems,"title":_vm.selectedContextItemsTitle,"default-collapsed":_vm.selectedContextItemsDefaultCollapsed,"variant":"assistant"}}):_vm._e(),_vm._v(" "),(_vm.error)?_c('div',{directives:[{name:"safe-html",rawName:"v-safe-html:[$options.safeHtmlConfigExtension]",value:(_vm.renderedError),expression:"renderedError",arg:_vm.$options.safeHtmlConfigExtension}],ref:"error-message"}):_c('div',[_c('div',{directives:[{name:"safe-html",rawName:"v-safe-html:[$options.safeHtmlConfigExtension]",value:(_vm.messageContent),expression:"messageContent",arg:_vm.$options.safeHtmlConfigExtension}],ref:"content"}),_vm._v(" "),(_vm.isAssistantMessage)?[(_vm.sources)?_c('documentation-sources',{attrs:{"sources":_vm.sources}}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"duo-chat-message-feedback gl-mt-4 gl-flex gl-items-end"},[(_vm.isChunkAndNotCancelled)?_c('gl-loading-icon',{staticClass:"gl-pt-4",attrs:{"variant":"dots","inline":""}}):_vm._e(),_vm._v(" "),(_vm.isNotChunkOrCancelled)?_c('gl-duo-user-feedback',{attrs:{"feedback-received":_vm.hasFeedback,"modal-title":_vm.$options.i18n.MODAL.TITLE,"modal-alert":_vm.$options.i18n.MODAL.ALERT_TEXT},on:{"feedback":_vm.logEvent},scopedSlots:_vm._u([{key:"feedback-extra-fields",fn:function(){return [_c('gl-form-group',{attrs:{"label":_vm.$options.i18n.MODAL.DID_WHAT,"optional":""}},[_c('gl-form-textarea',{attrs:{"placeholder":_vm.$options.i18n.MODAL.INTERACTION},model:{value:(_vm.didWhat),callback:function ($$v) {_vm.didWhat=$$v;},expression:"didWhat"}})],1),_vm._v(" "),_c('gl-form-group',{attrs:{"label":_vm.$options.i18n.MODAL.IMPROVE_WHAT,"optional":""}},[_c('gl-form-textarea',{attrs:{"placeholder":_vm.$options.i18n.MODAL.BETTER_RESPONSE},model:{value:(_vm.improveWhat),callback:function ($$v) {_vm.improveWhat=$$v;},expression:"improveWhat"}})],1)]},proxy:true}],null,false,419229417)}):_vm._e()],1)]:_vm._e()],2),_vm._v(" "),(_vm.displaySelectedContextItems && _vm.isUserMessage)?_c('gl-duo-chat-context-item-selections',{attrs:{"selections":_vm.selectedContextItems,"title":_vm.selectedContextItemsTitle,"default-collapsed":_vm.selectedContextItemsDefaultCollapsed,"variant":"user"}}):_vm._e()],1)],1)};
235
+ },on:{"insert-code-snippet":_vm.onInsertCodeSnippet}},[(_vm.error)?_c('gl-icon',{staticClass:"error-icon gl-border gl-mr-3 gl-shrink-0 gl-rounded-full gl-border-red-500 gl-text-red-600",attrs:{"aria-label":_vm.$options.i18n.MESSAGE_ERROR,"name":"status_warning_borderless","size":16,"data-testid":"error"}}):_vm._e(),_vm._v(" "),_c('div',{ref:"content-wrapper",class:{ 'has-error': _vm.error }},[(_vm.displaySelectedContextItems && _vm.isAssistantMessage)?_c('gl-duo-chat-context-item-selections',{attrs:{"selections":_vm.selectedContextItems,"title":_vm.selectedContextItemsTitle,"default-collapsed":_vm.selectedContextItemsDefaultCollapsed,"variant":"assistant"},on:{"get-content":_vm.onGetContextItemContent}}):_vm._e(),_vm._v(" "),(_vm.error)?_c('div',{directives:[{name:"safe-html",rawName:"v-safe-html:[$options.safeHtmlConfigExtension]",value:(_vm.renderedError),expression:"renderedError",arg:_vm.$options.safeHtmlConfigExtension}],ref:"error-message"}):_c('div',[_c('div',{directives:[{name:"safe-html",rawName:"v-safe-html:[$options.safeHtmlConfigExtension]",value:(_vm.messageContent),expression:"messageContent",arg:_vm.$options.safeHtmlConfigExtension}],ref:"content"}),_vm._v(" "),(_vm.isAssistantMessage)?[(_vm.sources)?_c('documentation-sources',{attrs:{"sources":_vm.sources}}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"duo-chat-message-feedback gl-mt-4 gl-flex gl-items-end"},[(_vm.isChunkAndNotCancelled)?_c('gl-loading-icon',{staticClass:"gl-pt-4",attrs:{"variant":"dots","inline":""}}):_vm._e(),_vm._v(" "),(_vm.isNotChunkOrCancelled)?_c('gl-duo-user-feedback',{attrs:{"feedback-received":_vm.hasFeedback,"modal-title":_vm.$options.i18n.MODAL.TITLE,"modal-alert":_vm.$options.i18n.MODAL.ALERT_TEXT},on:{"feedback":_vm.logEvent},scopedSlots:_vm._u([{key:"feedback-extra-fields",fn:function(){return [_c('gl-form-group',{attrs:{"label":_vm.$options.i18n.MODAL.DID_WHAT,"optional":""}},[_c('gl-form-textarea',{attrs:{"placeholder":_vm.$options.i18n.MODAL.INTERACTION},model:{value:(_vm.didWhat),callback:function ($$v) {_vm.didWhat=$$v;},expression:"didWhat"}})],1),_vm._v(" "),_c('gl-form-group',{attrs:{"label":_vm.$options.i18n.MODAL.IMPROVE_WHAT,"optional":""}},[_c('gl-form-textarea',{attrs:{"placeholder":_vm.$options.i18n.MODAL.BETTER_RESPONSE},model:{value:(_vm.improveWhat),callback:function ($$v) {_vm.improveWhat=$$v;},expression:"improveWhat"}})],1)]},proxy:true}],null,false,419229417)}):_vm._e()],1)]:_vm._e()],2),_vm._v(" "),(_vm.displaySelectedContextItems && _vm.isUserMessage)?_c('gl-duo-chat-context-item-selections',{attrs:{"selections":_vm.selectedContextItems,"title":_vm.selectedContextItemsTitle,"default-collapsed":_vm.selectedContextItemsDefaultCollapsed,"variant":"user"},on:{"get-content":_vm.onGetContextItemContent}}):_vm._e()],1)],1)};
230
236
  var __vue_staticRenderFns__ = [];
231
237
 
232
238
  /* style */
@@ -470,6 +470,14 @@ var script = {
470
470
  */
471
471
  this.$emit('insert-code-snippet', e);
472
472
  },
473
+ onGetContextItemContent(event) {
474
+ /**
475
+ * Emit get-context-item-content event that tells clients to load the full file content for a selected context item.
476
+ * The fully hydrated context item should be updated in the chat message context item.
477
+ * @param {*} event An event containing the message ID and context item to hydrate
478
+ */
479
+ this.$emit('get-context-item-content', event);
480
+ },
473
481
  closeContextItemsMenuOpen() {
474
482
  this.contextItemsMenuIsOpen = false;
475
483
  this.setPromptAndFocus();
@@ -490,7 +498,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
490
498
  {
491
499
  'gl-h-full': !_vm.hasMessages,
492
500
  'force-scroll-bar': _vm.hasMessages,
493
- } ],attrs:{"tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('gl-duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('gl-empty-state',{key:"empty-state",staticClass:"gl-flex-grow gl-justify-center",attrs:{"svg-path":_vm.$options.emptySvg,"svg-height":145,"title":_vm.emptyStateTitle},scopedSlots:_vm._u([{key:"description",fn:function(){return [_c('p',{staticClass:"gl-mb-3",attrs:{"data-testid":"gl-duo-chat-empty-state-description"}},[_vm._v("\n "+_vm._s(_vm.emptyStateDescription)+"\n ")]),_vm._v(" "),_c('p',{staticClass:"gl-mt-3 gl-text-sm gl-text-subtle",attrs:{"data-testid":"gl-duo-chat-empty-state-secondary-description"}},[_vm._v("\n "+_vm._s(_vm.emptyStateSecondaryDescription)+"\n ")])]},proxy:true}],null,false,460840487)}),_vm._v(" "),_c('gl-duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('gl-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)?_c('footer',{staticClass:"duo-chat-drawer-footer duo-chat-drawer-footer-sticky gl-border-t gl-bg-gray-10 gl-p-5",class:{ 'duo-chat-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_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.displaySubmitButton)?_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-base",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","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-base",attrs:{"icon":"stop","category":"primary","variant":"default","data-testid":"chat-prompt-cancel-button","aria-label":_vm.$options.i18n.CHAT_CANCEL_LABEL},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,677611116)},[_c('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-rounded-base gl-bg-white gl-align-top gl-shadow-inner-1-gray-400",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-gray-500"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute !gl-h-full gl-rounded-br-none gl-rounded-tr-none !gl-bg-transparent !gl-py-4 !gl-shadow-none",class:{ 'gl-truncate': !_vm.prompt },attrs:{"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"autofocus":""},on:{"compositionend":_vm.compositionEnd},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)])],1)],1):_vm._e()]):_vm._e()};
501
+ } ],attrs:{"tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('gl-duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"get-context-item-content":_vm.onGetContextItemContent}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('gl-empty-state',{key:"empty-state",staticClass:"gl-flex-grow gl-justify-center",attrs:{"svg-path":_vm.$options.emptySvg,"svg-height":145,"title":_vm.emptyStateTitle},scopedSlots:_vm._u([{key:"description",fn:function(){return [_c('p',{staticClass:"gl-mb-3",attrs:{"data-testid":"gl-duo-chat-empty-state-description"}},[_vm._v("\n "+_vm._s(_vm.emptyStateDescription)+"\n ")]),_vm._v(" "),_c('p',{staticClass:"gl-mt-3 gl-text-sm gl-text-subtle",attrs:{"data-testid":"gl-duo-chat-empty-state-secondary-description"}},[_vm._v("\n "+_vm._s(_vm.emptyStateSecondaryDescription)+"\n ")])]},proxy:true}],null,false,460840487)}),_vm._v(" "),_c('gl-duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('gl-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)?_c('footer',{staticClass:"duo-chat-drawer-footer duo-chat-drawer-footer-sticky gl-border-t gl-bg-gray-10 gl-p-5",class:{ 'duo-chat-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_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.displaySubmitButton)?_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-base",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","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-base",attrs:{"icon":"stop","category":"primary","variant":"default","data-testid":"chat-prompt-cancel-button","aria-label":_vm.$options.i18n.CHAT_CANCEL_LABEL},on:{"click":_vm.cancelPrompt}})]},proxy:true}],null,false,677611116)},[_c('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-rounded-base gl-bg-white gl-align-top gl-shadow-inner-1-gray-400",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-gray-500"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute !gl-h-full gl-rounded-br-none gl-rounded-tr-none !gl-bg-transparent !gl-py-4 !gl-shadow-none",class:{ 'gl-truncate': !_vm.prompt },attrs:{"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"autofocus":""},on:{"compositionend":_vm.compositionEnd},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)])],1)],1):_vm._e()]):_vm._e()};
494
502
  var __vue_staticRenderFns__ = [];
495
503
 
496
504
  /* style */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "95.0.0",
3
+ "version": "95.1.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -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>
@@ -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>
@@ -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}...',