@gitlab/ui 94.4.2 → 94.5.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 (59) hide show
  1. package/CHANGELOG.md +7 -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_menu/duo_chat_context_item_menu_search_item.js +10 -3
  4. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.js +12 -21
  5. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.js +5 -11
  6. package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +60 -3
  7. package/dist/components/experimental/duo/chat/components/duo_chat_context/utils.js +71 -1
  8. package/dist/index.css +2 -2
  9. package/dist/index.css.map +1 -1
  10. package/dist/tailwind.css +1 -1
  11. package/dist/tailwind.css.map +1 -1
  12. package/package.json +3 -3
  13. package/src/components/base/avatar_labeled/avatar_labeled.scss +1 -1
  14. package/src/components/base/breadcrumb/breadcrumb.scss +2 -2
  15. package/src/components/base/broadcast_message/broadcast_message.scss +1 -1
  16. package/src/components/base/button/button.scss +6 -6
  17. package/src/components/base/datepicker/datepicker.scss +7 -7
  18. package/src/components/base/daterange_picker/daterange_picker.scss +2 -2
  19. package/src/components/base/drawer/drawer.scss +3 -3
  20. package/src/components/base/dropdown/dropdown.scss +2 -2
  21. package/src/components/base/dropdown/dropdown_item.scss +4 -4
  22. package/src/components/base/filtered_search/filtered_search_suggestion_list.scss +1 -1
  23. package/src/components/base/filtered_search/filtered_search_term.scss +1 -1
  24. package/src/components/base/filtered_search/filtered_search_token.scss +3 -3
  25. package/src/components/base/filtered_search/filtered_search_token_segment.scss +2 -2
  26. package/src/components/base/form/form_checkbox/form_checkbox.scss +4 -4
  27. package/src/components/base/form/form_input/form_input.scss +3 -3
  28. package/src/components/base/form/form_select/form_select.scss +3 -3
  29. package/src/components/base/label/label.scss +4 -4
  30. package/src/components/base/loading_icon/loading_icon.scss +1 -1
  31. package/src/components/base/markdown/markdown.scss +3 -3
  32. package/src/components/base/markdown/markdown_typescale_demo.html +4 -4
  33. package/src/components/base/modal/modal.scss +1 -1
  34. package/src/components/base/new_dropdowns/dropdown.scss +3 -3
  35. package/src/components/base/new_dropdowns/dropdown_item.scss +2 -2
  36. package/src/components/base/pagination/pagination.scss +1 -1
  37. package/src/components/base/path/path.scss +3 -3
  38. package/src/components/base/popover/popover.scss +1 -1
  39. package/src/components/base/search_box_by_type/search_box_by_type.scss +1 -1
  40. package/src/components/base/segmented_control/segmented_control.scss +3 -3
  41. package/src/components/base/skeleton_loader/skeleton_loader.scss +1 -1
  42. package/src/components/base/tabs/tabs/tabs.scss +4 -4
  43. package/src/components/base/toast/toast.scss +1 -1
  44. package/src/components/base/toggle/toggle.scss +3 -3
  45. package/src/components/charts/heatmap/heatmap.scss +1 -1
  46. package/src/components/charts/legend/legend.scss +2 -2
  47. package/src/components/charts/tooltip/tooltip.scss +1 -1
  48. package/src/components/experimental/duo/chat/components/duo_chat_context/constants.js +4 -0
  49. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_item.vue +19 -6
  50. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.vue +33 -31
  51. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.vue +16 -18
  52. package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +64 -1
  53. package/src/components/experimental/duo/chat/components/duo_chat_context/utils.js +75 -0
  54. package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.scss +4 -4
  55. package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.scss +2 -2
  56. package/src/components/experimental/duo/chat/duo_chat.scss +4 -4
  57. package/src/components/shared_components/clear_icon_button/clear_icon_button.scss +1 -1
  58. package/src/scss/typescale/_index.scss +2 -2
  59. package/translations.js +6 -3
@@ -4,7 +4,7 @@
4
4
 
5
5
  .gl-new-dropdown-custom-toggle {
6
6
  *:first-child {
7
- @include gl-cursor-pointer;
7
+ @apply gl-cursor-pointer;
8
8
 
9
9
  &:focus {
10
10
  @apply gl-focus;
@@ -17,7 +17,7 @@
17
17
  @apply gl-bg-white;
18
18
  @apply gl-border-1 gl-border-solid gl-border-gray-200;
19
19
  @apply gl-rounded-lg;
20
- @include gl-shadow-md;
20
+ @apply gl-shadow-md;
21
21
  top: 0;
22
22
  left: 0;
23
23
  min-width: $gl-new-dropdown-min-width;
@@ -71,7 +71,7 @@
71
71
  }
72
72
 
73
73
  .gl-new-dropdown-button-text {
74
- @include gl-mr-auto;
74
+ @apply gl-mr-auto;
75
75
  @apply gl-overflow-hidden;
76
76
  @apply gl-text-ellipsis;
77
77
  }
@@ -1,5 +1,5 @@
1
1
  .gl-new-dropdown-item {
2
- @include gl-cursor-pointer;
2
+ @apply gl-cursor-pointer;
3
3
  @apply gl-px-2;
4
4
  @apply gl-my-1;
5
5
 
@@ -109,7 +109,7 @@
109
109
  }
110
110
 
111
111
  .gl-new-dropdown-item-text-wrapper {
112
- @include gl-min-w-0;
112
+ @apply gl-min-w-0;
113
113
  @apply gl-grow;
114
114
  @apply gl-py-3;
115
115
  }
@@ -51,7 +51,7 @@
51
51
  @apply gl-border-blue-500;
52
52
  @apply gl-text-white;
53
53
  @apply gl-z-2;
54
- @include gl-shadow-none;
54
+ @apply gl-shadow-none;
55
55
 
56
56
  &:focus {
57
57
  @include gl-focus();
@@ -41,7 +41,7 @@ $path-chevron-right-margin: px-to-rem(14px);
41
41
  }
42
42
 
43
43
  .gl-path-nav-list-item {
44
- @include gl-min-w-fit-content;
44
+ @apply gl-min-w-fit;
45
45
  padding-top: 1px;
46
46
  padding-bottom: 1px;
47
47
 
@@ -56,7 +56,7 @@ $path-chevron-right-margin: px-to-rem(14px);
56
56
  @apply gl-py-3;
57
57
  @apply gl-flex;
58
58
  @apply gl-text-base;
59
- @include gl-z-index-0;
59
+ @apply gl-z-0;
60
60
  @apply gl-font-bold;
61
61
  @apply gl-leading-normal;
62
62
  @apply gl-rounded-tl-base;
@@ -120,7 +120,7 @@ $path-chevron-right-margin: px-to-rem(14px);
120
120
  }
121
121
 
122
122
  &:disabled {
123
- @include gl-cursor-not-allowed;
123
+ @apply gl-cursor-not-allowed;
124
124
  background-color: var(--gl-color-alpha-0);
125
125
  box-shadow: none;
126
126
  color: var(--gl-action-disabled-foreground-color);
@@ -5,7 +5,7 @@ $gl-popover-max-width: $grid-size * 35;
5
5
  .gl-popover {
6
6
  max-width: $gl-popover-max-width;
7
7
  @apply gl-border-none;
8
- @include gl-shadow-md;
8
+ @apply gl-shadow-md;
9
9
  @apply gl-text-sm;
10
10
 
11
11
  &,
@@ -43,7 +43,7 @@ $gl-search-box-by-type-input-padding: 3.5 * $grid-size;
43
43
  @apply gl-leading-normal;
44
44
  @apply gl-pl-7;
45
45
  @apply gl-py-4;
46
- @include gl-shadow-none;
46
+ @apply gl-shadow-none;
47
47
  @apply gl-w-full;
48
48
  border-radius: 0;
49
49
  padding-right: calc(#{$gl-spacing-scale-6} + #{$gl-spacing-scale-2});
@@ -29,7 +29,7 @@
29
29
 
30
30
  .gl-segmented-control {
31
31
  label:not(.disabled) {
32
- @include gl-cursor-pointer;
32
+ @apply gl-cursor-pointer;
33
33
  }
34
34
 
35
35
  .btn-gl-segmented-button {
@@ -86,8 +86,8 @@
86
86
  &[disabled]:hover {
87
87
  @apply gl-text-gray-400;
88
88
  @include gl-inset-border-y-1-gray-200;
89
- @include gl-z-index-0;
90
- @include gl-cursor-not-allowed;
89
+ @apply gl-z-0;
90
+ @apply gl-cursor-not-allowed;
91
91
 
92
92
  &:first-child {
93
93
  @include gl-btn-gl-segmented-button-first;
@@ -4,7 +4,7 @@
4
4
 
5
5
  .gl-skeleton-loader {
6
6
  stop {
7
- @include gl-opacity-10;
7
+ @apply gl-opacity-10;
8
8
  }
9
9
 
10
10
  .background-stop {
@@ -47,12 +47,12 @@
47
47
  }
48
48
 
49
49
  &.disabled {
50
- @include gl-pointer-events-auto;
51
- @include gl-cursor-not-allowed;
50
+ @apply gl-pointer-events-auto;
51
+ @apply gl-cursor-not-allowed;
52
52
  color: var(--gl-action-disabled-foreground-color);
53
53
 
54
54
  &:hover {
55
- @include gl-shadow-none;
55
+ @apply gl-shadow-none;
56
56
  }
57
57
  }
58
58
 
@@ -160,5 +160,5 @@
160
160
  .gl-scrollable-tabs-nav {
161
161
  @apply gl-overflow-hidden;
162
162
  position: relative;
163
- @include gl-flex-nowrap;
163
+ @apply gl-flex-nowrap;
164
164
  }
@@ -52,7 +52,7 @@
52
52
  @apply gl-text-base;
53
53
  @apply gl-whitespace-nowrap;
54
54
  @apply gl-font-bold;
55
- @include gl-cursor-pointer;
55
+ @apply gl-cursor-pointer;
56
56
  }
57
57
 
58
58
  .gl-toast-close-button {
@@ -28,10 +28,10 @@ $toggle-height: 2.5 * $grid-size;
28
28
  @apply gl-inline-flex;
29
29
 
30
30
  &.is-disabled {
31
- @include gl-cursor-not-allowed;
31
+ @apply gl-cursor-not-allowed;
32
32
 
33
33
  .gl-toggle {
34
- @include gl-cursor-not-allowed;
34
+ @apply gl-cursor-not-allowed;
35
35
  @apply gl-bg-gray-200;
36
36
 
37
37
  .toggle-icon > svg {
@@ -85,7 +85,7 @@ $toggle-height: 2.5 * $grid-size;
85
85
  @apply gl-items-center;
86
86
  @apply gl-justify-center;
87
87
  @apply gl-border-0;
88
- @include gl-cursor-pointer;
88
+ @apply gl-cursor-pointer;
89
89
  @apply gl-bg-gray-600;
90
90
  position: relative;
91
91
  width: $toggle-width;
@@ -2,6 +2,6 @@
2
2
  position: relative;
3
3
 
4
4
  .gl-legend-inline-series {
5
- @include gl-pointer-events-none;
5
+ @apply gl-pointer-events-none;
6
6
  }
7
7
  }
@@ -29,7 +29,7 @@ $legend-body-h: $gl-spacing-scale-13 - $gl-spacing-scale-4;
29
29
 
30
30
  .gl-legend-inline {
31
31
  @apply gl-flex;
32
- @include gl-flex-wrap;
32
+ @apply gl-flex-wrap;
33
33
  @apply gl-shrink-0;
34
34
 
35
35
  .gl-legend-inline-series {
@@ -40,7 +40,7 @@ $legend-body-h: $gl-spacing-scale-13 - $gl-spacing-scale-4;
40
40
  @apply gl-pr-5;
41
41
 
42
42
  &:hover {
43
- @include gl-cursor-pointer;
43
+ @apply gl-cursor-pointer;
44
44
  @apply gl-underline;
45
45
  }
46
46
 
@@ -2,7 +2,7 @@
2
2
  position: absolute;
3
3
 
4
4
  > .popover {
5
- @include gl-min-w-0;
5
+ @apply gl-min-w-0;
6
6
  @apply gl-w-max;
7
7
  max-width: $chart-tooltip-max-width;
8
8
  }
@@ -1,3 +1,7 @@
1
1
  export const CONTEXT_ITEM_CATEGORY_ISSUE = 'issue';
2
2
  export const CONTEXT_ITEM_CATEGORY_MERGE_REQUEST = 'merge_request';
3
3
  export const CONTEXT_ITEM_CATEGORY_FILE = 'file';
4
+ export const CONTEXT_ITEM_CATEGORY_LOCAL_GIT = 'local_git';
5
+
6
+ export const CONTEXT_ITEM_LOCAL_GIT_COMMIT = 'commit';
7
+ export const CONTEXT_ITEM_LOCAL_GIT_DIFF = 'diff';
@@ -1,21 +1,25 @@
1
1
  <script>
2
2
  import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover.vue';
3
+ import GlTruncate from '../../../../../../utilities/truncate/truncate.vue';
3
4
  import GlIcon from '../../../../../../base/icon/icon.vue';
4
5
  import {
5
6
  categoryValidator,
6
7
  contextItemValidator,
8
+ formatGitItemSecondaryText,
7
9
  formatIssueId,
8
10
  formatMergeRequestId,
11
+ getContextItemIcon,
9
12
  } from '../utils';
10
13
  import {
14
+ CONTEXT_ITEM_CATEGORY_FILE,
11
15
  CONTEXT_ITEM_CATEGORY_ISSUE,
16
+ CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
12
17
  CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
13
- CONTEXT_ITEM_CATEGORY_FILE,
14
18
  } from '../constants';
15
19
 
16
20
  export default {
17
21
  name: 'GlDuoChatContextItemMenuSearchItem',
18
- components: { GlIcon, GlDuoChatContextItemPopover },
22
+ components: { GlTruncate, GlIcon, GlDuoChatContextItemPopover },
19
23
  props: {
20
24
  category: {
21
25
  type: Object,
@@ -40,18 +44,23 @@ export default {
40
44
  return formatIssueId(this.contextItem.metadata.iid);
41
45
  case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
42
46
  return formatMergeRequestId(this.contextItem.metadata.iid);
47
+ case CONTEXT_ITEM_CATEGORY_LOCAL_GIT:
48
+ return formatGitItemSecondaryText(this.contextItem);
43
49
  default:
44
50
  return '';
45
51
  }
46
52
  },
53
+ icon() {
54
+ return getContextItemIcon(this.contextItem, this.category);
55
+ },
47
56
  },
48
57
  };
49
58
  </script>
50
59
  <template>
51
60
  <div class="gl-flex gl-flex-col">
52
61
  <div class="gl-flex gl-items-center">
53
- <gl-icon :name="category.icon" class="gl-mr-2 gl-shrink-0" data-testid="category-icon" />
54
- <span>{{ title }}</span>
62
+ <gl-icon :name="icon" class="gl-mr-2 gl-shrink-0" data-testid="category-icon" />
63
+ <gl-truncate :text="title" class="gl-min-w-0" data-testid="item-title" />
55
64
  <gl-icon
56
65
  :id="`info-icon-${contextItem.id}`"
57
66
  name="information-o"
@@ -64,8 +73,12 @@ export default {
64
73
  placement="left"
65
74
  />
66
75
  </div>
67
- <div class="gl-mt-1 gl-shrink-0 gl-whitespace-nowrap gl-text-secondary">
68
- {{ secondaryText }}
76
+ <div
77
+ v-if="secondaryText"
78
+ class="gl-mt-1 gl-shrink-0 gl-whitespace-nowrap gl-text-secondary"
79
+ data-testid="item-secondary-text"
80
+ >
81
+ <gl-truncate :text="secondaryText" />
69
82
  </div>
70
83
  </div>
71
84
  </template>
@@ -1,18 +1,27 @@
1
1
  <script>
2
- import GlPopover from '../../../../../../base/popover/popover.vue';
2
+ import GlAlert from '../../../../../../base/alert/alert.vue';
3
3
  import GlIcon from '../../../../../../base/icon/icon.vue';
4
+ import GlPopover from '../../../../../../base/popover/popover.vue';
5
+ import GlTruncate from '../../../../../../utilities/truncate/truncate.vue';
4
6
  import { translate } from '../../../../../../../utils/i18n';
5
7
  import {
8
+ CONTEXT_ITEM_CATEGORY_FILE,
6
9
  CONTEXT_ITEM_CATEGORY_ISSUE,
10
+ CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
7
11
  CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
8
- CONTEXT_ITEM_CATEGORY_FILE,
9
12
  } from '../constants';
10
- import { formatIssueId, formatMergeRequestId } from '../utils';
11
- import GlAlert from '../../../../../../base/alert/alert.vue';
13
+ import {
14
+ formatGitItemSecondaryText,
15
+ formatIssueId,
16
+ formatMergeRequestId,
17
+ getContextItemIcon,
18
+ getContextItemTypeLabel,
19
+ } from '../utils';
12
20
 
13
21
  export default {
14
22
  name: 'DuoChatContextItemPopover',
15
23
  components: {
24
+ GlTruncate,
16
25
  GlAlert,
17
26
  GlIcon,
18
27
  GlPopover,
@@ -69,6 +78,11 @@ export default {
69
78
  filePathArray() {
70
79
  return this.filePath?.split('/');
71
80
  },
81
+ gitDetails() {
82
+ return this.contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT
83
+ ? formatGitItemSecondaryText(this.contextItem)
84
+ : null;
85
+ },
72
86
  isEnabled() {
73
87
  return this.contextItem.metadata.enabled !== false;
74
88
  },
@@ -79,24 +93,10 @@ export default {
79
93
  : translate('DuoChatContextItemPopover.DisabledReason', 'This item is disabled');
80
94
  },
81
95
  iconName() {
82
- switch (this.contextItem.category) {
83
- case 'merge_request':
84
- return 'merge-request';
85
- case 'issue':
86
- return 'issues';
87
- default:
88
- return 'project';
89
- }
96
+ return getContextItemIcon(this.contextItem);
90
97
  },
91
98
  itemTypeLabel() {
92
- switch (this.contextItem.category) {
93
- case 'merge_request':
94
- return translate('DuoChatContextItemPopover.MergeRequest', 'Merge request');
95
- case 'issue':
96
- return translate('DuoChatContextItemPopover.Issue', 'Issue');
97
- default:
98
- return translate('DuoChatContextItemPopover.File', 'Project file');
99
- }
99
+ return getContextItemTypeLabel(this.contextItem);
100
100
  },
101
101
  },
102
102
  methods: {
@@ -118,25 +118,27 @@ export default {
118
118
  class="gl-heading-3 gl-mb-1 gl-mt-2 gl-leading-1"
119
119
  data-testid="chat-context-popover-title"
120
120
  >
121
- {{ contextItem.metadata.title }}
121
+ {{ title }}
122
122
  </div>
123
- <div class="gl-font-normal gl-text-subtle">{{ itemTypeLabel }}</div>
123
+ <div v-if="itemTypeLabel" class="gl-font-normal gl-text-subtle">{{ itemTypeLabel }}</div>
124
124
  </div>
125
125
  </template>
126
126
  <div>
127
- <div v-if="filePath !== null">
128
- <gl-icon name="document" :size="12" variant="subtle" class="gl-mr-1" /><span
129
- class="gl-break-all"
130
- >{{ contextItem.metadata.project }}</span
131
- ><span v-for="(pathPart, index) in filePathArray" :key="pathPart" class="gl-break-all"
127
+ <div v-if="filePath">
128
+ <gl-icon name="document" :size="12" variant="subtle" />
129
+ <span class="gl-break-all">{{ contextItem.metadata.project }}</span>
130
+ <span v-for="(pathPart, index) in filePathArray" :key="pathPart" class="gl-break-all"
132
131
  >{{ pathPart }}{{ index + 1 < filePathArray.length ? '/' : '' }}</span
133
132
  >
134
133
  </div>
134
+ <div v-else-if="gitDetails" class="gl-flex gl-items-center" data-testid="git-details">
135
+ <gl-icon :name="iconName" :size="12" variant="subtle" class="gl-mr-1 gl-shrink-0" />
136
+ <gl-truncate :text="gitDetails" class="gl-min-w-0" />
137
+ </div>
135
138
  <div v-else>
136
- <gl-icon :name="iconName" :size="12" variant="subtle" class="gl-mr-1" /><span
137
- class="gl-break-all"
138
- >{{ contextItem.metadata.project }}</span
139
- ><span v-if="id !== null" class="gl-break-all">{{ formattedId }}</span>
139
+ <gl-icon v-if="iconName" :name="iconName" :size="12" variant="subtle" />
140
+ <span class="gl-break-all">{{ contextItem.metadata.project }}</span>
141
+ <span v-if="id" class="gl-break-all">{{ formattedId }}</span>
140
142
  </div>
141
143
  <gl-alert
142
144
  v-if="!isEnabled"
@@ -2,17 +2,14 @@
2
2
  import uniqueId from 'lodash/uniqueId';
3
3
  import GlIcon from '../../../../../../base/icon/icon.vue';
4
4
  import GlToken from '../../../../../../base/token/token.vue';
5
+ import GlTruncate from '../../../../../../utilities/truncate/truncate.vue';
5
6
  import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover.vue';
6
- import {
7
- CONTEXT_ITEM_CATEGORY_ISSUE,
8
- CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
9
- CONTEXT_ITEM_CATEGORY_FILE,
10
- } from '../constants';
11
- import { contextItemsValidator } from '../utils';
7
+ import { contextItemsValidator, getContextItemIcon } from '../utils';
12
8
 
13
9
  export default {
14
10
  name: 'GlDuoChatContextItemSelections',
15
11
  components: {
12
+ GlTruncate,
16
13
  GlIcon,
17
14
  GlDuoChatContextItemPopover,
18
15
  GlToken,
@@ -78,14 +75,7 @@ export default {
78
75
  },
79
76
  },
80
77
  methods: {
81
- getIconName(category) {
82
- const iconMap = {
83
- [CONTEXT_ITEM_CATEGORY_FILE]: 'document',
84
- [CONTEXT_ITEM_CATEGORY_ISSUE]: 'issues',
85
- [CONTEXT_ITEM_CATEGORY_MERGE_REQUEST]: 'merge-request',
86
- };
87
- return iconMap[category] || 'document';
88
- },
78
+ getContextItemIcon,
89
79
  toggleCollapse() {
90
80
  this.isCollapsed = !this.isCollapsed;
91
81
  },
@@ -122,13 +112,21 @@ export default {
122
112
  :key="item.id"
123
113
  :view-only="!removable"
124
114
  variant="default"
125
- class="gl-mb-2 gl-mr-2"
115
+ class="gl-mb-2 gl-mr-2 gl-max-w-full"
126
116
  :class="tokenVariantClasses"
127
117
  @close="onRemoveItem(item)"
128
118
  >
129
- <div :id="`context-item-${item.id}-${selectionsId}`" class="gl-flex gl-items-center">
130
- <gl-icon :name="getIconName(item.category)" :size="12" class="gl-mr-1" />
131
- {{ item.metadata.title }}
119
+ <div
120
+ :id="`context-item-${item.id}-${selectionsId}`"
121
+ class="gl-flex gl-min-w-0 gl-items-center"
122
+ >
123
+ <gl-icon
124
+ v-if="getContextItemIcon(item)"
125
+ :name="getContextItemIcon(item)"
126
+ :size="12"
127
+ class="gl-mr-1"
128
+ />
129
+ <gl-truncate :text="item.metadata.title" position="middle" />
132
130
  </div>
133
131
  <gl-duo-chat-context-item-popover
134
132
  :context-item="item"
@@ -2,10 +2,12 @@ import {
2
2
  CONTEXT_ITEM_CATEGORY_ISSUE,
3
3
  CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
4
4
  CONTEXT_ITEM_CATEGORY_FILE,
5
+ CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
5
6
  } from './constants';
6
7
 
7
8
  export const MOCK_CATEGORIES = [
8
9
  { label: 'Files', value: CONTEXT_ITEM_CATEGORY_FILE, icon: 'document' },
10
+ { label: 'Local Git', value: CONTEXT_ITEM_CATEGORY_LOCAL_GIT, icon: 'git' },
9
11
  { label: 'Issues', value: CONTEXT_ITEM_CATEGORY_ISSUE, icon: 'issues' },
10
12
  { label: 'Merge Requests', value: CONTEXT_ITEM_CATEGORY_MERGE_REQUEST, icon: 'merge-request' },
11
13
  ];
@@ -126,8 +128,69 @@ const mockMergeRequests = [
126
128
  MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED,
127
129
  ];
128
130
 
131
+ export const MOCK_CONTEXT_ITEM_GIT_DIFF = {
132
+ id: '6d88b466-0c38-48d6-b271-deda47f97cee',
133
+ category: CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
134
+ metadata: {
135
+ enabled: true,
136
+ title: 'Current working changes',
137
+ commitId: 'main',
138
+ repositoryName: 'example/garden',
139
+ gitType: 'diff',
140
+ },
141
+ };
142
+ export const MOCK_CONTEXT_ITEM_GIT_COMMIT = {
143
+ id: '20f8caf94cb8f5e5f9dbd1a9ac32702321de201b',
144
+ category: CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
145
+ metadata: {
146
+ enabled: true,
147
+ title: 'fix: some bug fix commit',
148
+ commitId: '20f8caf94cb8f5e5f9dbd1a9ac32702321de201b',
149
+ repositoryName: 'example/garden',
150
+ gitType: 'commit',
151
+ },
152
+ };
153
+
154
+ const mockGitItems = [
155
+ MOCK_CONTEXT_ITEM_GIT_DIFF,
156
+ {
157
+ id: 'diff-example/garden',
158
+ category: CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
159
+ metadata: {
160
+ enabled: true,
161
+ title: 'Diff from default branch',
162
+ commitId: 'main',
163
+ repositoryName: 'example/garden',
164
+ gitType: 'diff',
165
+ },
166
+ },
167
+ MOCK_CONTEXT_ITEM_GIT_COMMIT,
168
+ {
169
+ id: '32b9b56b6de75b32909986755fbc470f20fb6fc0',
170
+ category: CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
171
+ metadata: {
172
+ enabled: true,
173
+ title: 'feat: add cool new feature',
174
+ commitId: '32b9b56b6de75b32909986755fbc470f20fb6fc0',
175
+ repositoryName: 'example/garden',
176
+ gitType: 'commit',
177
+ },
178
+ },
179
+ {
180
+ id: '775d7efdce25c1af48c55abcadbefd1f181b92ce',
181
+ category: CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
182
+ metadata: {
183
+ enabled: true,
184
+ title: 'fix: stop foo from bar when baz because customers ding',
185
+ commitId: '775d7efdce25c1af48c55abcadbefd1f181b92ce',
186
+ repositoryName: 'example/garden',
187
+ gitType: 'commit',
188
+ },
189
+ },
190
+ ];
191
+
129
192
  export const getMockContextItems = () => {
130
- const allItems = [...mockFiles, ...mockIssues, ...mockMergeRequests];
193
+ const allItems = [...mockFiles, ...mockGitItems, ...mockIssues, ...mockMergeRequests];
131
194
 
132
195
  // put disabled items in the back
133
196
  const disabledItems = allItems.filter((item) => !item.metadata.enabled);
@@ -1,3 +1,13 @@
1
+ import { translate } from '../../../../../../utils/i18n';
2
+ import {
3
+ CONTEXT_ITEM_CATEGORY_FILE,
4
+ CONTEXT_ITEM_CATEGORY_ISSUE,
5
+ CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
6
+ CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
7
+ CONTEXT_ITEM_LOCAL_GIT_COMMIT,
8
+ CONTEXT_ITEM_LOCAL_GIT_DIFF,
9
+ } from './constants';
10
+
1
11
  export function categoryValidator(category) {
2
12
  return Boolean(category && category.value && category.label && category.icon);
3
13
  }
@@ -42,6 +52,71 @@ export function formatMergeRequestId(iid) {
42
52
  return `!${iid}`;
43
53
  }
44
54
 
55
+ function getGitItemIcon(contextItem) {
56
+ const iconMap = {
57
+ [CONTEXT_ITEM_LOCAL_GIT_COMMIT]: 'commit',
58
+ [CONTEXT_ITEM_LOCAL_GIT_DIFF]: 'comparison',
59
+ };
60
+ const { gitType } = contextItem.metadata;
61
+ return iconMap[gitType] || null;
62
+ }
63
+
64
+ /**
65
+ * Gets the icon name for a given contextItem.
66
+ */
67
+ export function getContextItemIcon(contextItem, category = { icon: null }) {
68
+ if (contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
69
+ const gitIcon = getGitItemIcon(contextItem);
70
+ if (gitIcon) return gitIcon;
71
+ }
72
+
73
+ if (category.icon) {
74
+ return category.icon;
75
+ }
76
+
77
+ const iconMap = {
78
+ [CONTEXT_ITEM_CATEGORY_FILE]: 'document',
79
+ [CONTEXT_ITEM_CATEGORY_ISSUE]: 'issues',
80
+ [CONTEXT_ITEM_CATEGORY_MERGE_REQUEST]: 'merge-request',
81
+ [CONTEXT_ITEM_CATEGORY_LOCAL_GIT]: 'git',
82
+ };
83
+
84
+ return iconMap[contextItem.category] || null;
85
+ }
86
+
87
+ export function getContextItemTypeLabel(contextItem) {
88
+ if (contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
89
+ switch (contextItem.metadata.gitType) {
90
+ case CONTEXT_ITEM_LOCAL_GIT_DIFF:
91
+ return translate('DuoChatContextItemTypeLabel.GitDiff', 'Local Git repository diff');
92
+ case CONTEXT_ITEM_LOCAL_GIT_COMMIT:
93
+ return translate('DuoChatContextItemTypeLabel.GitCommit', 'Local Git repository commit');
94
+ default:
95
+ return translate('DuoChatContextItemTypeLabel.GitDefault', 'Local Git repository');
96
+ }
97
+ }
98
+
99
+ switch (contextItem.category) {
100
+ case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
101
+ return translate('DuoChatContextItemTypeLabel.MergeRequest', 'Merge request');
102
+ case CONTEXT_ITEM_CATEGORY_ISSUE:
103
+ return translate('DuoChatContextItemTypeLabel.Issue', 'Issue');
104
+ case CONTEXT_ITEM_CATEGORY_FILE:
105
+ return translate('DuoChatContextItemTypeLabel.File', 'Project file');
106
+ default:
107
+ return '';
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Gets the secondary text line for a git context item, showing repository and commit ID
113
+ */
114
+ export function formatGitItemSecondaryText(contextItem) {
115
+ const { repositoryName, commitId } = contextItem.metadata;
116
+ const separator = commitId ? ' - ' : '';
117
+ return `${repositoryName}${separator}${commitId || ''}`;
118
+ }
119
+
45
120
  /**
46
121
  * Calculates a new index within a range. If the new index would fall out of bounds, wraps to the start/end of the range.
47
122
  * @param {number} currentIndex - The starting index.
@@ -7,7 +7,7 @@
7
7
  }
8
8
 
9
9
  .text-enter {
10
- @include gl-opacity-0;
10
+ @apply gl-opacity-0;
11
11
  }
12
12
 
13
13
  .text-enter-active {
@@ -15,11 +15,11 @@
15
15
  }
16
16
 
17
17
  .text-enter-to {
18
- @include gl-opacity-10;
18
+ @apply gl-opacity-10;
19
19
  }
20
20
 
21
21
  .text-leave {
22
- @include gl-opacity-10;
22
+ @apply gl-opacity-10;
23
23
  }
24
24
 
25
25
  .text-leave-active {
@@ -27,7 +27,7 @@
27
27
  }
28
28
 
29
29
  .text-leave-to {
30
- @include gl-opacity-0;
30
+ @apply gl-opacity-0;
31
31
  }
32
32
 
33
33
  &__dot {