@gitlab/duo-ui 0.3.0 → 1.0.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 (42) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/chat/components/duo_chat_context/constants.js +2 -1
  3. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.js +36 -9
  4. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.js +1 -1
  5. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_item.js +8 -16
  6. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_items_loading.js +1 -1
  7. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.js +12 -8
  8. package/dist/components/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.js +2 -2
  9. package/dist/components/chat/components/duo_chat_context/mock_context_data.js +118 -9
  10. package/dist/components/chat/components/duo_chat_context/utils.js +28 -2
  11. package/dist/components/chat/components/duo_chat_conversation/duo_chat_conversation.js +1 -1
  12. package/dist/components/chat/components/duo_chat_loader/duo_chat_loader.js +1 -1
  13. package/dist/components/chat/duo_chat.js +4 -4
  14. package/dist/components/workflow/components/duo_workflow_panel/duo_workflow_panel.js +1 -1
  15. package/dist/components/workflow/components/duo_workflow_prompt/duo_workflow_prompt.js +1 -1
  16. package/dist/components.css +1 -1
  17. package/dist/components.css.map +1 -1
  18. package/dist/config.js +44 -0
  19. package/dist/index.js +3 -0
  20. package/dist/tailwind.css +1 -1
  21. package/dist/tailwind.css.map +1 -1
  22. package/dist/utils/i18n.js +63 -0
  23. package/dist/utils/test_utils.js +33 -0
  24. package/package.json +2 -2
  25. package/src/components/chat/components/duo_chat_context/constants.js +1 -0
  26. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.vue +61 -6
  27. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.md +35 -0
  28. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.vue +1 -1
  29. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_item.vue +17 -25
  30. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu_search_items_loading.vue +1 -1
  31. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.vue +20 -15
  32. package/src/components/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.vue +10 -4
  33. package/src/components/chat/components/duo_chat_context/mock_context_data.js +148 -9
  34. package/src/components/chat/components/duo_chat_context/utils.js +32 -1
  35. package/src/components/chat/components/duo_chat_conversation/duo_chat_conversation.vue +1 -1
  36. package/src/components/chat/components/duo_chat_loader/duo_chat_loader.vue +1 -1
  37. package/src/components/chat/duo_chat.scss +19 -8
  38. package/src/components/chat/duo_chat.vue +15 -11
  39. package/src/components/workflow/components/duo_workflow_panel/duo_workflow_panel.vue +1 -1
  40. package/src/components/workflow/components/duo_workflow_prompt/duo_workflow_prompt.vue +1 -1
  41. package/src/utils/i18n.js +62 -0
  42. package/translations.js +2 -1
@@ -1,4 +1,4 @@
1
- import { translate } from '@gitlab/ui/dist/utils/i18n';
1
+ import { translate } from '../../../../utils/i18n';
2
2
  import {
3
3
  CONTEXT_ITEM_CATEGORY_FILE,
4
4
  CONTEXT_ITEM_CATEGORY_ISSUE,
@@ -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.
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { translate } from '@gitlab/ui/dist/utils/i18n';
2
+ import { translate } from '../../../../utils/i18n';
3
3
  import GlDuoChatMessage from '../duo_chat_message/duo_chat_message.vue';
4
4
 
5
5
  const i18n = {
@@ -1,6 +1,6 @@
1
1
  <script>
2
- import { translate } from '@gitlab/ui/dist/utils/i18n';
3
2
  import { GlSprintf } from '@gitlab/ui';
3
+ import { translate } from '../../../../utils/i18n';
4
4
  import { LOADING_TRANSITION_DURATION } from '../../constants';
5
5
 
6
6
  export const i18n = {
@@ -40,7 +40,9 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
40
40
  @apply gl-transition-all;
41
41
  position: fixed;
42
42
  @apply gl-h-full;
43
+ @apply gl-w-full;
43
44
  @apply gl-overflow-y-auto;
45
+ @apply gl-shadow-lg;
44
46
  @apply gl-text-base;
45
47
  @apply gl-leading-normal;
46
48
  @apply gl-flex;
@@ -48,13 +50,13 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
48
50
  }
49
51
 
50
52
  .duo-chat-drawer-header {
51
- @apply gl-border-b-solid gl-border-b-gray-100 gl-border-b-1;
53
+ @apply gl-border-b;
52
54
  }
53
55
 
54
56
  .duo-chat-drawer-header-sticky {
55
57
  top: 0;
56
58
  position: sticky;
57
- @apply gl-border-b-solid gl-border-b-gray-100 gl-border-b-1;
59
+ @apply gl-border-b;
58
60
  }
59
61
 
60
62
  .duo-chat-drawer-body {
@@ -63,14 +65,17 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
63
65
  // above the drawer when hovering interactive elements
64
66
  // see https://gitlab.com/gitlab-org/gitlab/-/issues/366558
65
67
  background-color: inherit;
68
+ // prevent scroll behind the chat
69
+ overscroll-behavior: contain;
66
70
  }
67
71
 
68
72
  .duo-chat-drawer-footer {
69
- @apply gl-border-t-solid gl-border-t-gray-100 gl-border-t-1;
73
+ @apply gl-border-t;
70
74
  @apply gl-p-5;
71
75
  }
72
76
 
73
77
  .duo-chat-drawer-footer-sticky {
78
+ @apply gl-bg-white;
74
79
  bottom: 0;
75
80
  position: sticky;
76
81
  }
@@ -78,14 +83,14 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
78
83
  .duo-chat-drawer-body-scrim-on-footer {
79
84
  &::before {
80
85
  background: $duo-chat-scrim-gradient;
81
- top: -$gl-border-size-1;
86
+ top: 0;
82
87
  @apply -gl-translate-y-full;
83
88
  content: '';
84
89
  left: 0;
85
90
  position: absolute;
86
91
  @apply gl-pointer-events-none;
87
92
  @apply gl-w-full;
88
- @apply gl-h-7;
93
+ @apply gl-h-5;
89
94
  }
90
95
  }
91
96
 
@@ -125,9 +130,12 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
125
130
  @apply gl-flex-col;
126
131
  max-height: 240px;
127
132
  overflow: hidden;
133
+ background: var(--gl-control-background-color-default);
134
+ box-shadow: inset 0 0 0 $gl-border-size-1 var(--gl-control-border-color-default);
135
+ border-radius: px-to-rem(20px);
128
136
 
129
137
  &:focus-within {
130
- @include gl-focus($color: $gray-900);
138
+ @include gl-focus($color: var(--gl-control-border-color-focus));
131
139
  }
132
140
 
133
141
  .gl-form-textarea.form-control {
@@ -135,13 +143,16 @@ $duo-chat-scrim-gradient: linear-gradient(to bottom, rgba($gray-10, 0), $gray-10
135
143
  resize: none;
136
144
  max-height: 240px;
137
145
  padding-right: 40px;
146
+ border-radius: px-to-rem(20px);
138
147
  }
139
148
 
140
149
  &::after {
141
150
  content: attr(data-value) ' ';
142
151
  @apply gl-invisible;
143
- @apply gl-whitespace-pre-wrap;
144
- @apply gl-py-4;
152
+ @apply gl-p-4;
153
+ @apply gl-font-regular;
154
+ padding-right: 40px;
155
+ word-break: break-word;
145
156
  }
146
157
  }
147
158
 
@@ -15,7 +15,7 @@ import {
15
15
  GlSafeHtmlDirective as SafeHtml,
16
16
  } from '@gitlab/ui';
17
17
 
18
- import { translate } from '@gitlab/ui/dist/utils/i18n';
18
+ import { translate } from '../../utils/i18n';
19
19
  import {
20
20
  badgeTypes,
21
21
  badgeTypeValidator,
@@ -46,7 +46,7 @@ export const i18n = {
46
46
  ),
47
47
  CHAT_PROMPT_PLACEHOLDER_WITH_COMMANDS: translate(
48
48
  'GlDuoChat.chatPromptPlaceholderWithCommands',
49
- 'Type "/" for slash commands'
49
+ 'Type /help to learn more'
50
50
  ),
51
51
  CHAT_SUBMIT_LABEL: translate('GlDuoChat.chatSubmitLabel', 'Send chat message.'),
52
52
  CHAT_CANCEL_LABEL: translate('GlDuoChat.chatCancelLabel', 'Cancel'),
@@ -542,14 +542,14 @@ export default {
542
542
  <aside
543
543
  v-if="!isHidden"
544
544
  id="chat-component"
545
- class="markdown-code-block duo-chat-drawer duo-chat gl-border-t gl-border-l gl-bottom-0 gl-max-h-full gl-shadow-none"
545
+ class="markdown-code-block duo-chat-drawer duo-chat gl-bottom-0 gl-max-h-full"
546
546
  role="complementary"
547
547
  data-testid="chat-component"
548
548
  >
549
549
  <header
550
550
  v-if="showHeader"
551
551
  data-testid="chat-header"
552
- class="duo-chat-drawer-header duo-chat-drawer-header-sticky gl-z-200 gl-bg-gray-10 !gl-p-0"
552
+ class="duo-chat-drawer-header duo-chat-drawer-header-sticky gl-z-200 gl-border-0 gl-bg-default !gl-p-0"
553
553
  >
554
554
  <div class="drawer-title gl-flex gl-items-center gl-justify-start gl-p-5">
555
555
  <h3 class="gl-my-0 gl-text-size-h2">{{ title }}</h3>
@@ -590,11 +590,15 @@ export default {
590
590
  </gl-alert>
591
591
  </header>
592
592
 
593
- <div class="duo-chat-drawer-body" data-testid="chat-history" @scroll="handleScrollingTrottled">
593
+ <div
594
+ class="duo-chat-drawer-body gl-bg-default"
595
+ data-testid="chat-history"
596
+ @scroll="handleScrollingTrottled"
597
+ >
594
598
  <transition-group
595
599
  tag="section"
596
600
  name="message"
597
- class="duo-chat-history gl-flex gl-flex-col gl-justify-end gl-bg-gray-10"
601
+ class="duo-chat-history gl-flex gl-flex-col gl-justify-end"
598
602
  :class="[
599
603
  {
600
604
  'gl-h-full': !hasMessages,
@@ -646,7 +650,7 @@ export default {
646
650
  <footer
647
651
  v-if="isChatAvailable"
648
652
  data-testid="chat-footer"
649
- class="duo-chat-drawer-footer duo-chat-drawer-footer-sticky gl-border-t gl-bg-gray-10 gl-p-5"
653
+ class="duo-chat-drawer-footer duo-chat-drawer-footer-sticky gl-border-0 gl-bg-default gl-p-5"
650
654
  :class="{ 'duo-chat-drawer-body-scrim-on-footer': !scrolledToBottom }"
651
655
  >
652
656
  <gl-form data-testid="chat-prompt-form" @submit.stop.prevent="sendChatPrompt">
@@ -665,7 +669,7 @@ export default {
665
669
 
666
670
  <gl-form-input-group>
667
671
  <div
668
- class="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"
672
+ class="duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-align-top"
669
673
  :data-value="prompt"
670
674
  >
671
675
  <gl-card
@@ -683,7 +687,7 @@ export default {
683
687
  >
684
688
  <span class="gl-flex gl-justify-between">
685
689
  <span class="gl-block">{{ command.name }}</span>
686
- <small class="gl-pl-3 gl-text-right gl-italic gl-text-gray-500">{{
690
+ <small class="gl-pl-3 gl-text-right gl-italic gl-text-subtle">{{
687
691
  command.description
688
692
  }}</small>
689
693
  </span>
@@ -709,7 +713,7 @@ export default {
709
713
  icon="paper-airplane"
710
714
  category="primary"
711
715
  variant="confirm"
712
- class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-base"
716
+ class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full"
713
717
  type="submit"
714
718
  data-testid="chat-prompt-submit-button"
715
719
  :aria-label="$options.i18n.CHAT_SUBMIT_LABEL"
@@ -719,7 +723,7 @@ export default {
719
723
  icon="stop"
720
724
  category="primary"
721
725
  variant="default"
722
- class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-base"
726
+ class="!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full"
723
727
  data-testid="chat-prompt-cancel-button"
724
728
  :aria-label="$options.i18n.CHAT_CANCEL_LABEL"
725
729
  @click="cancelPrompt"
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { GlIcon, GlCollapse, GlButton, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
3
- import { translate } from '@gitlab/ui/dist/utils/i18n';
3
+ import { translate } from '../../../../utils/i18n';
4
4
 
5
5
  export default {
6
6
  name: 'GlDuoWorkflowPanel',
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { GlButton, GlFormGroup, GlFormTextarea, GlFormInput } from '@gitlab/ui';
3
- import { translate } from '@gitlab/ui/dist/utils/i18n';
3
+ import { translate } from '../../../../utils/i18n';
4
4
  import GlDuoWorkflowPanel from '../duo_workflow_panel/duo_workflow_panel.vue';
5
5
 
6
6
  export default {
@@ -0,0 +1,62 @@
1
+ import escape from 'lodash/escape';
2
+ import { i18n } from '../config';
3
+
4
+ const defaultPluralHandler = (n, singleValue, pluralValue) => {
5
+ const value = n === 1 ? singleValue : pluralValue;
6
+
7
+ return value.replace(/%d/g, n);
8
+ };
9
+
10
+ /**
11
+ * Mark a label as translatable.
12
+ *
13
+ * @param {string} key Translation key to be leveraged by the consumer to provide a generic translation at configuration time.
14
+ * @param {string} defaultValue A fallback value to be relied on if the consumer doesn't have translation capabilities.
15
+ * @returns {string} The translated label.
16
+ */
17
+ export const translate = (key, defaultValue) => i18n[key] ?? defaultValue;
18
+
19
+ /**
20
+ * Marks a label as translatable and pluralized.
21
+ *
22
+ * @param {*} key Translation key to be leveraged by the consumer to provide a generic translation at configuration time.
23
+ * @param {*} singularValue The singular value to be relied on if the consumer doesn't have translation capabilities.
24
+ * @param {*} pluralValue The plural value to be relied on if the consumer doesn't have translation capabilities.
25
+ * @returns {function} A function that takes a number and returns the pluralized translated label.
26
+ */
27
+ export const translatePlural = (key, singularValue, pluralValue) => {
28
+ if (i18n[key]) {
29
+ return i18n[key];
30
+ }
31
+ return (x) => defaultPluralHandler(x, singularValue, pluralValue);
32
+ };
33
+
34
+ /**
35
+ * Very limited implementation of sprintf supporting only named parameters.
36
+ * Copied from the GitLab repo: https://gitlab.com/gitlab-org/gitlab/-/blob/0dff8b02accb3dccbf6cd31236834c37013aad59/app/assets/javascripts/locale/sprintf.js.
37
+ * @param {string} input - (translated) text with parameters (e.g. '%{num_users} users use us')
38
+ * @param {Object.<string, string|number>} [parameters] - object mapping parameter names to values (e.g. { num_users: 5 })
39
+ * @param {boolean} [escapeParameters=true] - whether parameter values should be escaped (see https://lodash.com/docs/4.17.15#escape)
40
+ * @returns {string} the text with parameters replaces (e.g. '5 users use us')
41
+ * @see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
42
+ * @see https://gitlab.com/gitlab-org/gitlab-foss/issues/37992
43
+ */
44
+ export function sprintf(input, parameters, escapeParameters = true) {
45
+ let output = input;
46
+
47
+ output = output.replace(/%+/g, '%');
48
+
49
+ if (parameters) {
50
+ const mappedParameters = new Map(Object.entries(parameters));
51
+
52
+ mappedParameters.forEach((key, parameterName) => {
53
+ const parameterValue = mappedParameters.get(parameterName);
54
+ const escapedParameterValue = escapeParameters ? escape(parameterValue) : parameterValue;
55
+ // Pass the param value as a function to ignore special replacement patterns like $` and $'.
56
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#syntax
57
+ output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), () => escapedParameterValue);
58
+ });
59
+ }
60
+
61
+ return output;
62
+ }
package/translations.js CHANGED
@@ -18,8 +18,9 @@ export default {
18
18
  'GlDuoChat.chatEmptyStateSecondaryDesc': 'Responses may be inaccurate. Verify before use.',
19
19
  'GlDuoChat.chatEmptyStateTitle': 'Ask a question',
20
20
  'GlDuoChat.chatPromptPlaceholderDefault': 'GitLab Duo Chat',
21
- 'GlDuoChat.chatPromptPlaceholderWithCommands': 'Type "/" for slash commands',
21
+ 'GlDuoChat.chatPromptPlaceholderWithCommands': 'Type /help to learn more',
22
22
  'GlDuoChat.chatSubmitLabel': 'Send chat message.',
23
+ 'GlDuoChatContextItemDetailsModal.contentErrorMessage': 'Item content could not be displayed.',
23
24
  'GlDuoChatContextItemDetailsModal.title': 'Preview',
24
25
  'GlDuoChatContextItemMenu.emptyStateMessage': 'No results found',
25
26
  'GlDuoChatContextItemMenu.loadingMessage': 'Loading...',