@gitlab/ui 101.2.0 → 101.3.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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_item.js +8 -15
  3. package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.js +11 -6
  4. package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +44 -6
  5. package/dist/components/experimental/duo/chat/components/duo_chat_context/utils.js +27 -1
  6. package/dist/index.css +1 -1
  7. package/dist/index.css.map +1 -1
  8. package/dist/tokens/build/js/tokens.dark.js +571 -572
  9. package/dist/tokens/build/js/tokens.js +571 -572
  10. package/dist/tokens/css/tokens.css +943 -944
  11. package/dist/tokens/css/tokens.dark.css +943 -944
  12. package/dist/tokens/js/tokens.dark.js +571 -572
  13. package/dist/tokens/js/tokens.js +571 -572
  14. package/dist/tokens/json/tokens.dark.json +3034 -3034
  15. package/dist/tokens/json/tokens.json +3034 -3034
  16. package/dist/tokens/scss/_tokens.dark.scss +943 -944
  17. package/dist/tokens/scss/_tokens.scss +943 -944
  18. package/dist/tokens/scss/_tokens_custom_properties.scss +1 -2
  19. package/dist/tokens/tailwind/tokens.cjs +1 -2
  20. package/dist/tokens/tokens_story.js +1 -1
  21. package/dist/tokens/tokens_table.js +4 -6
  22. package/package.json +3 -6
  23. package/src/components/base/broadcast_message/broadcast_message.scss +9 -0
  24. package/src/components/base/card/card.scss +8 -7
  25. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.md +35 -0
  26. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_item.vue +17 -24
  27. package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.vue +19 -13
  28. package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +44 -6
  29. package/src/components/experimental/duo/chat/components/duo_chat_context/utils.js +31 -0
  30. package/src/tokens/build/css/tokens.css +943 -944
  31. package/src/tokens/build/css/tokens.dark.css +943 -944
  32. package/src/tokens/build/js/tokens.dark.js +571 -572
  33. package/src/tokens/build/js/tokens.js +571 -572
  34. package/src/tokens/build/json/tokens.dark.json +3034 -3034
  35. package/src/tokens/build/json/tokens.json +3034 -3034
  36. package/src/tokens/build/scss/_tokens.dark.scss +943 -944
  37. package/src/tokens/build/scss/_tokens.scss +943 -944
  38. package/src/tokens/build/scss/_tokens_custom_properties.scss +1 -2
  39. package/src/tokens/build/tailwind/tokens.cjs +1 -2
  40. package/src/tokens/tokens_story.vue +7 -7
  41. package/src/tokens/tokens_table.vue +4 -4
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Automatically generated
3
- * Do not edit directly
2
+ * Do not edit directly, this file was auto-generated.
4
3
  */
5
4
 
6
5
  $gl-action-disabled-foreground-color: var(--gl-action-disabled-foreground-color);
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Automatically generated
3
- * Do not edit directly
2
+ * Do not edit directly, this file was auto-generated.
4
3
  */
5
4
 
6
5
  const baseColors = {
@@ -54,7 +54,7 @@ var script = {
54
54
  const __vue_script__ = script;
55
55
 
56
56
  /* template */
57
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:_vm.containerClass},[_c('ul',{staticClass:"gl-m-0 gl-list-none gl-p-0"},_vm._l((_vm.tokens),function(token){return _c('li',{key:token.name,staticClass:"gl-flex gl-flex-wrap gl-items-center gl-justify-between gl-gap-3 gl-p-3",class:_vm.getClasses(token.value),style:(_vm.getStyle(token.value))},[_c('code',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],staticClass:"gl-text-inherit",attrs:{"title":token.comment}},[_vm._v("\n "+_vm._s(_vm.getTokenName(token))+"\n ")]),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-items-center gl-gap-3"},[(token.deprecated)?_c('gl-badge',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],attrs:{"title":token.comment,"variant":"danger"}},[_vm._v("\n Deprecated\n ")]):_vm._e(),_vm._v(" "),_c('code',{staticClass:"gl-text-inherit"},[_vm._v(_vm._s(token.value))]),_vm._v(" "),(_vm.isHex(token.value))?_c('gl-color-contrast',{attrs:{"foreground":token.value,"background":_vm.darkBackground}}):_vm._e(),_vm._v(" "),(_vm.isHex(token.value))?_c('gl-color-contrast',{attrs:{"foreground":token.value,"background":_vm.lightBackground}}):_vm._e()],1)])}),0)])};
57
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:_vm.containerClass},[_c('ul',{staticClass:"gl-m-0 gl-list-none gl-p-0"},_vm._l((_vm.tokens),function(token){return _c('li',{key:token.name,staticClass:"gl-flex gl-flex-wrap gl-items-center gl-justify-between gl-gap-3 gl-p-3",class:_vm.getClasses(token.$value),style:(_vm.getStyle(token.$value))},[_c('code',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],staticClass:"gl-text-inherit",attrs:{"title":token.comment}},[_vm._v("\n "+_vm._s(_vm.getTokenName(token))+"\n ")]),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-items-center gl-gap-3"},[(token.deprecated)?_c('gl-badge',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],attrs:{"title":token.comment,"variant":"danger"}},[_vm._v("\n Deprecated\n ")]):_vm._e(),_vm._v(" "),_c('code',{staticClass:"gl-text-inherit"},[_vm._v(_vm._s(token.$value))]),_vm._v(" "),(_vm.isHex(token.$value))?_c('gl-color-contrast',{attrs:{"foreground":token.$value,"background":_vm.darkBackground}}):_vm._e(),_vm._v(" "),(_vm.isHex(token.$value))?_c('gl-color-contrast',{attrs:{"foreground":token.$value,"background":_vm.lightBackground}}):_vm._e()],1)])}),0)])};
58
58
  var __vue_staticRenderFns__ = [];
59
59
 
60
60
  /* style */
@@ -84,23 +84,21 @@ var script = {
84
84
  return value;
85
85
  },
86
86
  getValueLabel(token) {
87
- const {
88
- value
89
- } = token.original;
87
+ const value = token.original.$value;
90
88
  if (this.isAliasObject(value)) {
91
89
  return this.getAliasValueName(value[this.selectedMode]);
92
90
  }
93
91
  if (this.isAliasValue(value)) {
94
92
  return this.getAliasValueName(value);
95
93
  }
96
- return token.value;
94
+ return token.$value;
97
95
  },
98
96
  transformTokenToTableColumns(token) {
99
97
  return {
100
98
  id: token.path.filter(Boolean).join('-'),
101
99
  name: this.formatTokenName(this.selectedPlatform, token),
102
100
  type: token.$type,
103
- value: token.value,
101
+ value: token.$value,
104
102
  valueLabel: this.getValueLabel(token),
105
103
  deprecated: token.deprecated,
106
104
  description: token.comment
@@ -110,7 +108,7 @@ var script = {
110
108
  const tokensArray = [];
111
109
  Object.keys(tokens).forEach(key => {
112
110
  const token = tokens[key];
113
- if (token.value) {
111
+ if (token.$value) {
114
112
  tokensArray.push(this.transformTokenToTableColumns(token));
115
113
  } else {
116
114
  tokensArray.push(...this.transformTokensToTableRows(token));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "101.2.0",
3
+ "version": "101.3.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "prebuild": "run-s build-tokens build-migration-script",
34
34
  "prepare": "run-s build-tokens",
35
35
  "copy-fonts": "make copy-fonts",
36
- "build-tokens": "node ./bin/build_tokens.js",
36
+ "build-tokens": "node ./bin/build_tokens.mjs",
37
37
  "build-migration-script": "esbuild --bundle --platform=node --target=esnext --outfile=bin/migrate_custom_utils_to_tw.bundled.mjs --format=esm --banner:js=\"import { createRequire as __gl__createRequire } from 'node:module'; const require = __gl__createRequire(import.meta.url);\" bin/migrate_custom_utils_to_tw.mjs",
38
38
  "clean": "rm -r dist storybook",
39
39
  "cy:a11y": "cypress run --browser chrome --env grepTags=@a11y",
@@ -71,7 +71,6 @@
71
71
  "markdownlint:fix": "yarn markdownlint --fix",
72
72
  "lint": "run-p prettier eslint stylelint markdownlint",
73
73
  "lint:fix": "run-s eslint:fix prettier:fix stylelint:fix markdownlint:fix",
74
- "generate:component": "plop",
75
74
  "translations:collect": "make translations.js",
76
75
  "tailwind-config-viewer:start": "tailwind-config-viewer -o",
77
76
  "tailwind-config-viewer:export": "tailwind-config-viewer export ./tailwind-config-viewer-static"
@@ -158,7 +157,6 @@
158
157
  "glob": "10.3.3",
159
158
  "globby": "^11.1.0",
160
159
  "identity-obj-proxy": "^3.0.0",
161
- "inquirer-select-directory": "^1.2.0",
162
160
  "jest": "^29.7.0",
163
161
  "jest-circus": "29.7.0",
164
162
  "jest-environment-jsdom": "29.7.0",
@@ -171,7 +169,6 @@
171
169
  "pikaday": "^1.8.0",
172
170
  "playwright": "^1.48.1",
173
171
  "playwright-core": "^1.48.1",
174
- "plop": "^2.5.4",
175
172
  "postcss": "8.4.28",
176
173
  "postcss-loader": "^7.0.2",
177
174
  "postcss-scss": "4.0.4",
@@ -191,7 +188,7 @@
191
188
  "start-server-and-test": "^1.10.6",
192
189
  "storybook": "^7.6.20",
193
190
  "storybook-dark-mode": "4.0.2",
194
- "style-dictionary": "^3.8.0",
191
+ "style-dictionary": "^4.1.4",
195
192
  "stylelint": "16.8.1",
196
193
  "tailwind-config-viewer": "2.0.4",
197
194
  "tailwindcss": "3.4.4",
@@ -110,6 +110,15 @@ $gl-broadcast-message-padding-x: var(--gl-broadcast-message-padding-x, 0px);
110
110
  &-content {
111
111
  @apply gl-flex;
112
112
  @apply gl-justify-center;
113
+
114
+ a {
115
+ @apply gl-text-inherit;
116
+ @apply gl-underline;
117
+ }
118
+
119
+ p:last-of-type {
120
+ @apply gl-mb-0;
121
+ }
113
122
  }
114
123
 
115
124
  &-icon {
@@ -1,11 +1,11 @@
1
1
  .gl-card {
2
- background-color: var(--gl-background-color-default);
2
+ @apply gl-bg-subtle;
3
3
  @apply gl-border-solid;
4
4
  @apply gl-rounded-base;
5
5
  @apply gl-flex;
6
6
  @apply gl-flex-col;
7
7
  @apply gl-break-words;
8
- position: relative;
8
+ @apply gl-relative;
9
9
  @apply gl-text-base;
10
10
  }
11
11
 
@@ -13,20 +13,21 @@
13
13
  .gl-card-header,
14
14
  .gl-card-footer {
15
15
  @apply gl-border-1;
16
- border-color: var(--gl-border-color-default);
16
+ @apply gl-border-section;
17
17
  }
18
18
 
19
19
  .gl-card-header,
20
20
  .gl-card-body,
21
21
  .gl-card-footer {
22
- @apply gl-p-5;
22
+ @apply gl-px-5;
23
+ @apply gl-py-4;
23
24
  }
24
25
 
25
26
  .gl-card-header {
26
27
  @apply gl-border-b-solid;
27
28
  border-top-left-radius: calc($gl-border-radius-base - $gl-border-size-1);
28
29
  border-top-right-radius: calc($gl-border-radius-base - $gl-border-size-1);
29
- color: var(--gl-text-color-heading);
30
+ @apply gl-text-heading;
30
31
  }
31
32
 
32
33
  .gl-card-footer {
@@ -37,10 +38,10 @@
37
38
 
38
39
  .gl-card-header,
39
40
  .gl-card-footer {
40
- background-color: var(--gl-background-color-subtle);
41
+ @apply gl-bg-section;
41
42
  }
42
43
 
43
44
  .gl-card-body,
44
45
  .gl-card-footer {
45
- color: var(--gl-text-color-default);
46
+ @apply gl-text-default;
46
47
  }
@@ -0,0 +1,35 @@
1
+ Allows selecting and removing context items for the conversation.
2
+
3
+ **Note:**
4
+ Keyboard events don't work properly in this story (independently of the main GlDuoChat
5
+ component)- test in the main `GlDuoChat` interactive story with the /include command.
6
+
7
+ ## AIContextItem type
8
+
9
+ The component expects items with specific display properties:
10
+
11
+ ```typescript
12
+ export type AIContextItem = {
13
+ id: string;
14
+ category: 'file' | 'snippet' | 'issue' | 'merge_request' | 'dependency';
15
+
16
+ content?: string; // some categories allow loading/displaying content in the details-modal
17
+
18
+ metadata: {
19
+ icon: string; // should be a valid gitlab-ui icon name
20
+ title: string;
21
+ secondaryText: string;
22
+ subTypeLabel: string;
23
+
24
+ // Additional properties some categories have to help differentiate results
25
+ project?: string;
26
+ repositoryName?: string;
27
+
28
+ // items may be disabled, e.g. if they belong to a non-Duo-enabled project
29
+ enabled: boolean;
30
+ disabledReasons?: string[];
31
+ };
32
+ };
33
+ ```
34
+
35
+ For the editor extensions, these types are defined [in the language server](https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/blob/main/src/common/ai_context_management/index.ts)
@@ -5,21 +5,15 @@ import GlIcon from '../../../../../../base/icon/icon.vue';
5
5
  import {
6
6
  categoryValidator,
7
7
  contextItemValidator,
8
- formatGitItemSecondaryText,
9
- formatIssueId,
10
- formatMergeRequestId,
11
8
  getContextItemIcon,
9
+ getContextItemSecondaryText,
10
+ getContextItemSource,
12
11
  } from '../utils';
13
- import {
14
- CONTEXT_ITEM_CATEGORY_FILE,
15
- CONTEXT_ITEM_CATEGORY_ISSUE,
16
- CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
17
- CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
18
- } from '../constants';
12
+ import GlBadge from '../../../../../../base/badge/badge.vue';
19
13
 
20
14
  export default {
21
15
  name: 'GlDuoChatContextItemMenuSearchItem',
22
- components: { GlTruncate, GlIcon, GlDuoChatContextItemPopover },
16
+ components: { GlBadge, GlTruncate, GlIcon, GlDuoChatContextItemPopover },
23
17
  props: {
24
18
  category: {
25
19
  type: Object,
@@ -37,22 +31,14 @@ export default {
37
31
  return this.contextItem.metadata?.title || '';
38
32
  },
39
33
  secondaryText() {
40
- switch (this.category.value) {
41
- case CONTEXT_ITEM_CATEGORY_FILE:
42
- return this.contextItem.metadata.relativePath;
43
- case CONTEXT_ITEM_CATEGORY_ISSUE:
44
- return formatIssueId(this.contextItem.metadata.iid);
45
- case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
46
- return formatMergeRequestId(this.contextItem.metadata.iid);
47
- case CONTEXT_ITEM_CATEGORY_LOCAL_GIT:
48
- return formatGitItemSecondaryText(this.contextItem);
49
- default:
50
- return '';
51
- }
34
+ return getContextItemSecondaryText(this.contextItem);
52
35
  },
53
36
  icon() {
54
37
  return getContextItemIcon(this.contextItem, this.category);
55
38
  },
39
+ itemSource() {
40
+ return getContextItemSource(this.contextItem);
41
+ },
56
42
  },
57
43
  };
58
44
  </script>
@@ -75,10 +61,17 @@ export default {
75
61
  </div>
76
62
  <div
77
63
  v-if="secondaryText"
78
- class="gl-mt-1 gl-shrink-0 gl-whitespace-nowrap gl-text-secondary"
64
+ class="gl-align-items-center gl-mt-1 gl-flex gl-shrink-0 gl-whitespace-nowrap gl-text-secondary"
79
65
  data-testid="item-secondary-text"
80
66
  >
81
- <gl-truncate :text="secondaryText" />
67
+ <gl-badge
68
+ v-if="itemSource"
69
+ variant="neutral"
70
+ class="gl-mr-1"
71
+ data-testid="context-item-source"
72
+ >{{ itemSource }}</gl-badge
73
+ >
74
+ <gl-truncate class="gl-min-w-0" position="middle" :text="secondaryText" />
82
75
  </div>
83
76
  </div>
84
77
  </template>
@@ -7,20 +7,22 @@ import { translate } from '../../../../../../../utils/i18n';
7
7
  import {
8
8
  CONTEXT_ITEM_CATEGORY_FILE,
9
9
  CONTEXT_ITEM_CATEGORY_ISSUE,
10
- CONTEXT_ITEM_CATEGORY_LOCAL_GIT,
11
10
  CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
12
11
  } from '../constants';
13
12
  import {
14
- formatGitItemSecondaryText,
15
13
  formatIssueId,
16
14
  formatMergeRequestId,
17
15
  getContextItemIcon,
16
+ getContextItemSecondaryText,
17
+ getContextItemSource,
18
18
  getContextItemTypeLabel,
19
19
  } from '../utils';
20
+ import GlBadge from '../../../../../../base/badge/badge.vue';
20
21
 
21
22
  export default {
22
23
  name: 'DuoChatContextItemPopover',
23
24
  components: {
25
+ GlBadge,
24
26
  GlTruncate,
25
27
  GlAlert,
26
28
  GlIcon,
@@ -78,11 +80,6 @@ export default {
78
80
  filePathArray() {
79
81
  return this.filePath?.split('/');
80
82
  },
81
- gitDetails() {
82
- return this.contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT
83
- ? formatGitItemSecondaryText(this.contextItem)
84
- : null;
85
- },
86
83
  isEnabled() {
87
84
  return this.contextItem.metadata.enabled !== false;
88
85
  },
@@ -98,6 +95,12 @@ export default {
98
95
  itemTypeLabel() {
99
96
  return getContextItemTypeLabel(this.contextItem);
100
97
  },
98
+ secondaryText() {
99
+ return getContextItemSecondaryText(this.contextItem);
100
+ },
101
+ itemSource() {
102
+ return getContextItemSource(this.contextItem);
103
+ },
101
104
  },
102
105
  methods: {
103
106
  translate,
@@ -120,21 +123,24 @@ export default {
120
123
  >
121
124
  {{ title }}
122
125
  </div>
123
- <div v-if="itemTypeLabel" class="gl-font-normal gl-text-subtle">{{ itemTypeLabel }}</div>
126
+ <div v-if="itemTypeLabel" class="gl-flex gl-items-center gl-font-normal gl-text-subtle">
127
+ <gl-truncate :text="itemTypeLabel" class="gl-min-w-0" />
128
+ </div>
124
129
  </div>
125
130
  </template>
126
131
  <div>
127
- <div v-if="filePath">
132
+ <div v-if="secondaryText" class="gl-flex gl-items-center">
133
+ <gl-icon :name="iconName" :size="12" variant="subtle" class="gl-mr-1 gl-shrink-0" />
134
+ <gl-badge v-if="itemSource" class="gl-mr-1">{{ itemSource }}</gl-badge>
135
+ <gl-truncate :text="secondaryText" class="gl-min-w-0" />
136
+ </div>
137
+ <div v-else-if="filePath">
128
138
  <gl-icon name="document" :size="12" variant="subtle" />
129
139
  <span class="gl-break-all">{{ contextItem.metadata.project }}</span>
130
140
  <span v-for="(pathPart, index) in filePathArray" :key="pathPart" class="gl-break-all"
131
141
  >{{ pathPart }}{{ index + 1 < filePathArray.length ? '/' : '' }}</span
132
142
  >
133
143
  </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>
138
144
  <div v-else>
139
145
  <gl-icon v-if="iconName" :name="iconName" :size="12" variant="subtle" />
140
146
  <span class="gl-break-all">{{ contextItem.metadata.project }}</span>
@@ -37,6 +37,9 @@ export const MOCK_CONTEXT_ITEM_FILE = {
37
37
  metadata: {
38
38
  enabled: true,
39
39
  title: 'strawberry.ts',
40
+ secondaryText: 'src/plants/strawberry.ts',
41
+ subTypeLabel: 'Project file',
42
+ icon: 'document',
40
43
  project: 'example/garden',
41
44
  relativePath: 'src/plants/strawberry.ts',
42
45
  },
@@ -48,6 +51,9 @@ export const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
48
51
  metadata: {
49
52
  enabled: false,
50
53
  title: 'motorbike.cs',
54
+ secondaryText: '/src/VehicleFoo/motorbike.cs',
55
+ subTypeLabel: 'Project file',
56
+ icon: 'document',
51
57
  project: 'example/vehicles',
52
58
  relativePath: '/src/VehicleFoo/motorbike.cs',
53
59
  },
@@ -60,6 +66,9 @@ const mockFiles = [
60
66
  metadata: {
61
67
  enabled: true,
62
68
  title: 'potato.ts',
69
+ secondaryText: '/src/plants/potato.ts',
70
+ subTypeLabel: 'Project file',
71
+ icon: 'document',
63
72
  project: 'example/garden',
64
73
  relativePath: '/src/plants/potato.ts',
65
74
  },
@@ -73,7 +82,9 @@ export const MOCK_CONTEXT_ITEM_ISSUE = {
73
82
  metadata: {
74
83
  enabled: true,
75
84
  title: 'Implement watering schedule',
76
- project: 'example/garden',
85
+ secondaryText: 'example/garden#1234',
86
+ subTypeLabel: 'Issue',
87
+ icon: 'issues',
77
88
  iid: 1234,
78
89
  },
79
90
  };
@@ -85,7 +96,9 @@ export const MOCK_CONTEXT_ITEM_ISSUE_DISABLED = {
85
96
  enabled: false,
86
97
  disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
87
98
  title: `Fix vehicle colours and make them look real nice and colourful won't that be wonderful wow this issue title is really long I sure hope it's gonna wrap OK`,
88
- project: 'example/vehicle',
99
+ secondaryText: 'example/vehicle#91011',
100
+ subTypeLabel: 'Issue',
101
+ icon: 'issues',
89
102
  iid: 91011,
90
103
  },
91
104
  };
@@ -98,7 +111,9 @@ const mockIssues = [
98
111
  metadata: {
99
112
  enabled: true,
100
113
  title: 'Refactor plant growth rates',
101
- project: 'example/garden',
114
+ secondaryText: 'example/garden#5678',
115
+ subTypeLabel: 'Issue',
116
+ icon: 'issues',
102
117
  iid: 5678,
103
118
  },
104
119
  },
@@ -111,7 +126,9 @@ export const MOCK_CONTEXT_ITEM_MERGE_REQUEST = {
111
126
  metadata: {
112
127
  enabled: true,
113
128
  title: 'Improve database performance',
114
- project: 'example/garden',
129
+ secondaryText: 'example/garden!1122',
130
+ subTypeLabel: 'Merge request',
131
+ icon: 'merge-request',
115
132
  iid: 1122,
116
133
  },
117
134
  };
@@ -122,7 +139,9 @@ export const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
122
139
  enabled: false,
123
140
  disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
124
141
  title: 'Fix broken layout at small viewports',
125
- project: 'example/vehicle',
142
+ secondaryText: 'example/vehicle!5566',
143
+ subTypeLabel: 'Merge request',
144
+ icon: 'merge-request',
126
145
  iid: 5566,
127
146
  },
128
147
  };
@@ -136,7 +155,9 @@ const mockMergeRequests = [
136
155
  enabled: false,
137
156
  disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
138
157
  title: 'Add vehicle registration details',
139
- project: 'example/vehicle',
158
+ secondaryText: 'example/vehicle!3344',
159
+ subTypeLabel: 'Merge request',
160
+ icon: 'merge-request',
140
161
  iid: 3344,
141
162
  },
142
163
  },
@@ -149,6 +170,10 @@ export const MOCK_CONTEXT_ITEM_GIT_DIFF = {
149
170
  metadata: {
150
171
  enabled: true,
151
172
  title: 'Current working changes',
173
+ secondaryText: 'main',
174
+ subType: 'local_git',
175
+ subTypeLabel: 'Local Git repository diff',
176
+ icon: 'comparison',
152
177
  commitId: 'main',
153
178
  repositoryName: 'example/garden',
154
179
  gitType: 'diff',
@@ -160,6 +185,10 @@ export const MOCK_CONTEXT_ITEM_GIT_COMMIT = {
160
185
  metadata: {
161
186
  enabled: true,
162
187
  title: 'fix: some bug fix commit',
188
+ secondaryText: '20f8caf94cb8f5e5f9dbd1a9ac32702321de201b',
189
+ subType: 'local_git',
190
+ subTypeLabel: 'Local Git repository commit',
191
+ icon: 'commit',
163
192
  commitId: '20f8caf94cb8f5e5f9dbd1a9ac32702321de201b',
164
193
  repositoryName: 'example/garden',
165
194
  gitType: 'commit',
@@ -174,6 +203,9 @@ const mockGitItems = [
174
203
  metadata: {
175
204
  enabled: true,
176
205
  title: 'Diff from default branch',
206
+ secondaryText: 'main',
207
+ subTypeLabel: 'Local Git repository diff',
208
+ icon: 'comparison',
177
209
  commitId: 'main',
178
210
  repositoryName: 'example/garden',
179
211
  gitType: 'diff',
@@ -186,6 +218,9 @@ const mockGitItems = [
186
218
  metadata: {
187
219
  enabled: true,
188
220
  title: 'feat: add cool new feature',
221
+ secondaryText: '32b9b56b6de75b32909986755fbc470f20fb6fc0',
222
+ subTypeLabel: 'Local Git repository commit',
223
+ icon: 'commit',
189
224
  commitId: '32b9b56b6de75b32909986755fbc470f20fb6fc0',
190
225
  repositoryName: 'example/garden',
191
226
  gitType: 'commit',
@@ -197,6 +232,9 @@ const mockGitItems = [
197
232
  metadata: {
198
233
  enabled: true,
199
234
  title: 'fix: stop foo from bar when baz because customers ding',
235
+ secondaryText: '775d7efdce25c1af48c55abcadbefd1f181b92ce',
236
+ subTypeLabel: 'Local Git repository commit',
237
+ icon: 'commit',
200
238
  commitId: '775d7efdce25c1af48c55abcadbefd1f181b92ce',
201
239
  repositoryName: 'example/garden',
202
240
  gitType: 'commit',
@@ -52,6 +52,10 @@ export function formatMergeRequestId(iid) {
52
52
  return `!${iid}`;
53
53
  }
54
54
 
55
+ export function getContextItemSource(contextItem) {
56
+ return contextItem.metadata.repositoryName || contextItem.metadata.project || null;
57
+ }
58
+
55
59
  function getGitItemIcon(contextItem) {
56
60
  const iconMap = {
57
61
  [CONTEXT_ITEM_LOCAL_GIT_COMMIT]: 'commit',
@@ -65,6 +69,10 @@ function getGitItemIcon(contextItem) {
65
69
  * Gets the icon name for a given contextItem.
66
70
  */
67
71
  export function getContextItemIcon(contextItem, category = { icon: null }) {
72
+ if (contextItem.metadata.icon) {
73
+ return contextItem.metadata.icon;
74
+ }
75
+
68
76
  if (contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
69
77
  const gitIcon = getGitItemIcon(contextItem);
70
78
  if (gitIcon) return gitIcon;
@@ -85,6 +93,10 @@ export function getContextItemIcon(contextItem, category = { icon: null }) {
85
93
  }
86
94
 
87
95
  export function getContextItemTypeLabel(contextItem) {
96
+ if (contextItem.metadata.subTypeLabel) {
97
+ return contextItem.metadata.subTypeLabel;
98
+ }
99
+
88
100
  if (contextItem.category === CONTEXT_ITEM_CATEGORY_LOCAL_GIT) {
89
101
  switch (contextItem.metadata.gitType) {
90
102
  case CONTEXT_ITEM_LOCAL_GIT_DIFF:
@@ -117,6 +129,25 @@ export function formatGitItemSecondaryText(contextItem) {
117
129
  return `${repositoryName}${separator}${commitId || ''}`;
118
130
  }
119
131
 
132
+ export function getContextItemSecondaryText(contextItem) {
133
+ if (contextItem.metadata.secondaryText) {
134
+ return contextItem.metadata.secondaryText;
135
+ }
136
+
137
+ switch (contextItem.category) {
138
+ case CONTEXT_ITEM_CATEGORY_FILE:
139
+ return contextItem.metadata.relativePath;
140
+ case CONTEXT_ITEM_CATEGORY_ISSUE:
141
+ return formatIssueId(contextItem.metadata.iid);
142
+ case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
143
+ return formatMergeRequestId(contextItem.metadata.iid);
144
+ case CONTEXT_ITEM_CATEGORY_LOCAL_GIT:
145
+ return formatGitItemSecondaryText(contextItem);
146
+ default:
147
+ return '';
148
+ }
149
+ }
150
+
120
151
  /**
121
152
  * Calculates a new index within a range. If the new index would fall out of bounds, wraps to the start/end of the range.
122
153
  * @param {number} currentIndex - The starting index.