@gitlab/duo-ui 10.19.0 → 10.21.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 (27) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/components/agentic_chat/agentic_duo_chat.js +8 -3
  3. package/dist/components/chat/components/duo_chat_header/duo_chat_header.js +28 -3
  4. package/dist/components/chat/components/duo_chat_message_tool_approval/components/create_commit_tool_params.js +148 -0
  5. package/dist/components/chat/components/duo_chat_message_tool_approval/components/create_issue_tool_params.js +88 -0
  6. package/dist/components/chat/components/duo_chat_message_tool_approval/components/create_merge_request_tool_params.js +83 -0
  7. package/dist/components/chat/components/duo_chat_message_tool_approval/components/pre_block.js +38 -0
  8. package/dist/components/chat/components/duo_chat_message_tool_approval/components/run_command_tool_params.js +62 -0
  9. package/dist/components/chat/components/duo_chat_message_tool_approval/message_tool_approval.js +46 -8
  10. package/dist/components/chat/mock_data.js +85 -1
  11. package/dist/components.css +1 -1
  12. package/dist/components.css.map +1 -1
  13. package/dist/tailwind.css +1 -1
  14. package/dist/tailwind.css.map +1 -1
  15. package/dist/utils/object.js +9 -0
  16. package/package.json +4 -4
  17. package/src/components/agentic_chat/agentic_duo_chat.vue +9 -2
  18. package/src/components/chat/components/duo_chat_header/duo_chat_header.vue +64 -12
  19. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_commit_tool_params.vue +155 -0
  20. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_issue_tool_params.vue +80 -0
  21. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_merge_request_tool_params.vue +74 -0
  22. package/src/components/chat/components/duo_chat_message_tool_approval/components/pre_block.vue +5 -0
  23. package/src/components/chat/components/duo_chat_message_tool_approval/components/run_command_tool_params.vue +30 -0
  24. package/src/components/chat/components/duo_chat_message_tool_approval/message_tool_approval.vue +143 -88
  25. package/src/components/chat/mock_data.js +99 -0
  26. package/src/utils/object.js +4 -0
  27. package/translations.js +24 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/duo-ui",
3
- "version": "10.19.0",
3
+ "version": "10.21.0",
4
4
  "description": "Duo UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -93,16 +93,16 @@
93
93
  },
94
94
  "devDependencies": {
95
95
  "@arkweid/lefthook": "0.7.7",
96
- "@babel/core": "^7.28.3",
96
+ "@babel/core": "^7.28.4",
97
97
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
98
98
  "@babel/plugin-proposal-optional-chaining": "^7.21.0",
99
99
  "@babel/preset-env": "^7.28.3",
100
100
  "@babel/preset-react": "^7.27.1",
101
- "@gitlab/eslint-plugin": "21.2.0",
101
+ "@gitlab/eslint-plugin": "21.2.1",
102
102
  "@gitlab/fonts": "^1.3.1",
103
103
  "@gitlab/stylelint-config": "6.2.2",
104
104
  "@gitlab/svgs": "^3.146.0",
105
- "@gitlab/ui": "^121.0.1",
105
+ "@gitlab/ui": "^122.1.2",
106
106
  "@jest/test-sequencer": "^29.7.0",
107
107
  "@rollup/plugin-commonjs": "^11.1.0",
108
108
  "@rollup/plugin-node-resolve": "^7.1.3",
@@ -149,6 +149,12 @@ export default {
149
149
  minHeight: 400,
150
150
  }),
151
151
  },
152
+ agents: {
153
+ type: Array,
154
+
155
+ required: false,
156
+ default: () => [],
157
+ },
152
158
  /**
153
159
  * The title of the chat/feature.
154
160
  */
@@ -489,8 +495,8 @@ export default {
489
495
  onGoBack() {
490
496
  this.$emit('back-to-list');
491
497
  },
492
- onNewChat() {
493
- this.$emit('new-chat');
498
+ onNewChat(agent) {
499
+ this.$emit('new-chat', agent);
494
500
 
495
501
  this.$nextTick(() => {
496
502
  this.focusChatInput();
@@ -802,6 +808,7 @@ export default {
802
808
  :should-render-resizable="shouldRenderResizable"
803
809
  :badge-type="isMultithreaded ? null : badgeType"
804
810
  :session-id="sessionId"
811
+ :agents="agents"
805
812
  @go-back="onGoBack"
806
813
  @new-chat="onNewChat"
807
814
  @close="hideChat"
@@ -10,6 +10,7 @@ import {
10
10
  GlSafeHtmlDirective as SafeHtml,
11
11
  GlTooltipDirective,
12
12
  GlToast,
13
+ GlDisclosureDropdown,
13
14
  } from '@gitlab/ui';
14
15
  import { translate } from '../../../../utils/i18n';
15
16
  import { VIEW_TYPES } from './constants';
@@ -46,6 +47,7 @@ export default {
46
47
  GlDropdown,
47
48
  GlDropdownItem,
48
49
  GlExperimentBadge,
50
+ GlDisclosureDropdown,
49
51
  },
50
52
  directives: {
51
53
  SafeHtml,
@@ -96,13 +98,33 @@ export default {
96
98
  type: String,
97
99
  required: true,
98
100
  },
101
+ agents: {
102
+ type: Array,
103
+ required: false,
104
+ default: () => [],
105
+ },
99
106
  },
100
107
  computed: {
101
108
  VIEW_TYPES() {
102
109
  return VIEW_TYPES;
103
110
  },
111
+ hasManyAgents() {
112
+ return this.agents.length > 1;
113
+ },
114
+ hasAgents() {
115
+ return this.agents.length > 0;
116
+ },
104
117
  },
105
118
  methods: {
119
+ startNewChat(agent) {
120
+ if (agent) {
121
+ this.$emit('new-chat', agent);
122
+ } else if (this.hasAgents) {
123
+ this.$emit('new-chat', this.agents[0]);
124
+ } else {
125
+ this.$emit('new-chat');
126
+ }
127
+ },
106
128
  async copySessionIdToClipboard() {
107
129
  try {
108
130
  await navigator.clipboard.writeText(this.sessionId);
@@ -134,18 +156,48 @@ export default {
134
156
  </div>
135
157
 
136
158
  <div class="gl-flex gl-gap-3">
137
- <gl-button
138
- v-if="isMultithreaded && (activeThreadId || currentView === VIEW_TYPES.LIST)"
139
- v-gl-tooltip
140
- :title="$options.i18n.CHAT_NEW_TOOLTIP"
141
- data-testid="chat-new-button"
142
- variant="confirm"
143
- category="tertiary"
144
- size="small"
145
- icon="duo-chat-new"
146
- :aria-label="$options.i18n.CHAT_NEW_LABEL"
147
- @click="$emit('new-chat')"
148
- />
159
+ <template
160
+ v-if="
161
+ isMultithreaded && (activeThreadId || currentView === VIEW_TYPES.LIST || hasManyAgents)
162
+ "
163
+ >
164
+ <gl-disclosure-dropdown
165
+ v-if="hasManyAgents"
166
+ :title="$options.i18n.CHAT_NEW_TOOLTIP"
167
+ :toggle-text="$options.i18n.CHAT_NEW_TOOLTIP"
168
+ :items="agents"
169
+ data-testid="chat-new-button"
170
+ variant="confirm"
171
+ category="tertiary"
172
+ size="small"
173
+ icon="duo-chat-new"
174
+ text-sr-only
175
+ :aria-label="$options.i18n.CHAT_NEW_LABEL"
176
+ no-caret
177
+ @action="startNewChat"
178
+ >
179
+ <template #list-item="{ item }">
180
+ <span class="gl-flex gl-flex-col">
181
+ <span clas="gl-block">{{ item.name }}</span>
182
+ <span class="gl-overflow-hidden gl-text-ellipsis gl-whitespace-nowrap gl-text-sm">{{
183
+ item.description
184
+ }}</span>
185
+ </span>
186
+ </template>
187
+ </gl-disclosure-dropdown>
188
+ <gl-button
189
+ v-else
190
+ v-gl-tooltip
191
+ :title="$options.i18n.CHAT_NEW_TOOLTIP"
192
+ data-testid="chat-new-button"
193
+ variant="confirm"
194
+ category="tertiary"
195
+ size="small"
196
+ icon="duo-chat-new"
197
+ :aria-label="$options.i18n.CHAT_NEW_LABEL"
198
+ @click="startNewChat"
199
+ />
200
+ </template>
149
201
  <gl-button
150
202
  v-if="isMultithreaded && currentView === VIEW_TYPES.CHAT"
151
203
  v-gl-tooltip
@@ -0,0 +1,155 @@
1
+ <script>
2
+ import { GlSprintf, GlAccordion, GlAccordionItem } from '@gitlab/ui';
3
+ import { translate, translatePlural, sprintf } from '../../../../../utils/i18n';
4
+ import PreBlock from './pre_block.vue';
5
+
6
+ export default {
7
+ name: 'CreateCommitToolParams',
8
+ components: {
9
+ GlSprintf,
10
+ GlAccordion,
11
+ GlAccordionItem,
12
+ PreBlock,
13
+ },
14
+ props: {
15
+ projectId: {
16
+ type: [String, Number],
17
+ required: true,
18
+ },
19
+ projectPath: {
20
+ type: String,
21
+ required: false,
22
+ default: '',
23
+ },
24
+ branch: {
25
+ type: String,
26
+ required: true,
27
+ },
28
+ startBranch: {
29
+ type: String,
30
+ required: false,
31
+ default: '',
32
+ },
33
+ commitMessage: {
34
+ type: String,
35
+ required: true,
36
+ },
37
+ actions: {
38
+ type: Array,
39
+ required: true,
40
+ },
41
+ },
42
+ computed: {
43
+ actionsCount() {
44
+ return this.actions.length;
45
+ },
46
+ actionsCountMessage() {
47
+ return sprintf(
48
+ translatePlural(
49
+ 'CreateCommitToolParams.actionsCountMessage',
50
+ 'The commit contains %{count} file change.',
51
+ 'The commit contains %{count} file changes.'
52
+ )(this.actionsCount),
53
+ {
54
+ count: this.actionsCount,
55
+ }
56
+ );
57
+ },
58
+ },
59
+ methods: {
60
+ getActionTitle({ action, file_path: filePath, previous_path: previousPath }) {
61
+ switch (action) {
62
+ case 'create':
63
+ return sprintf(this.$options.i18n.CREATE_FILE_ACTION_LABEL, { filePath });
64
+ case 'update':
65
+ return sprintf(this.$options.i18n.UPDATE_FILE_ACTION_LABEL, { filePath });
66
+ case 'delete':
67
+ return sprintf(this.$options.i18n.DELETE_FILE_ACTION_LABEL, { filePath });
68
+ case 'move':
69
+ return sprintf(this.$options.i18n.MOVE_FILE_ACTION_LABEL, { filePath: previousPath });
70
+ case 'chmod':
71
+ return sprintf(this.$options.i18n.CHMOD_FILE_ACTION_LABEL, { filePath });
72
+ default:
73
+ return sprintf(this.$options.i18n.UNKNOWN_FILE_ACTION_LABEL, { filePath });
74
+ }
75
+ },
76
+ getActionContent({ action, content, encoding }) {
77
+ if (!['create', 'update'].includes(action)) {
78
+ return this.$options.i18n.ACTION_WITH_NO_CONTENT;
79
+ }
80
+
81
+ return (!encoding || encoding === 'text') && content ? content : '';
82
+ },
83
+ },
84
+ i18n: {
85
+ COMMIT_SUMMARY_MESSAGE: translate(
86
+ 'CreateCommitToolParams.commitSummaryMessage',
87
+ 'Create a commit in the branch %{branch} and project %{project}.'
88
+ ),
89
+ READ_COMMIT_MESSAGE: translate(
90
+ 'CreateCommitToolParams.readCommitMessage',
91
+ 'Read commit message'
92
+ ),
93
+ CREATE_FILE_ACTION_LABEL: translate(
94
+ 'CreateCommitToolParams.createFileActionLabel',
95
+ 'Create file %{filePath}'
96
+ ),
97
+ UPDATE_FILE_ACTION_LABEL: translate(
98
+ 'CreateCommitToolParams.updateFileActionLabel',
99
+ 'Update file %{filePath}'
100
+ ),
101
+ DELETE_FILE_ACTION_LABEL: translate(
102
+ 'CreateCommitToolParams.deleteFileActionLabel',
103
+ 'Delete file %{filePath}'
104
+ ),
105
+ MOVE_FILE_ACTION_LABEL: translate(
106
+ 'CreateCommitToolParams.moveFileActionLabel',
107
+ 'Move file %{filePath}'
108
+ ),
109
+ CHMOD_FILE_ACTION_LABEL: translate(
110
+ 'CreateCommitToolParams.chmodFileActionLabel',
111
+ 'Change permissions for file %{filePath}'
112
+ ),
113
+ UNKNOWN_FILE_ACTION_LABEL: translate(
114
+ 'CreateCommitToolParams.unknownFileActionLabel',
115
+ 'Modify file %{filePath}'
116
+ ),
117
+ ACTION_WITH_NO_CONTENT: translate(
118
+ 'CreateCommitToolParams.actionWithNoContent',
119
+ 'This action does not have any content.'
120
+ ),
121
+ EXPAND_CHANGES: translate('CreateCommitToolParams.expandFileChanges', 'Expand file changes'),
122
+ },
123
+ };
124
+ </script>
125
+ <template>
126
+ <div class="gl-flex gl-flex-col">
127
+ <div>
128
+ <gl-sprintf :message="$options.i18n.COMMIT_SUMMARY_MESSAGE">
129
+ <template #project>
130
+ <code>{{ projectPath || projectId }}</code>
131
+ </template>
132
+ <template #branch>
133
+ <code>{{ branch }}</code>
134
+ </template>
135
+ </gl-sprintf>
136
+ {{ actionsCountMessage }}
137
+ </div>
138
+ <gl-accordion class="-gl-ml-2 gl-mt-3" :header-level="3">
139
+ <gl-accordion-item :title="$options.i18n.READ_COMMIT_MESSAGE">
140
+ <pre-block>{{ commitMessage }}</pre-block>
141
+ </gl-accordion-item>
142
+ <gl-accordion-item :title="$options.i18n.EXPAND_CHANGES">
143
+ <gl-accordion :header-level="4">
144
+ <gl-accordion-item
145
+ v-for="(action, index) in actions"
146
+ :key="index"
147
+ :title="getActionTitle(action)"
148
+ >
149
+ <pre-block>{{ getActionContent(action) }}</pre-block>
150
+ </gl-accordion-item>
151
+ </gl-accordion>
152
+ </gl-accordion-item>
153
+ </gl-accordion>
154
+ </div>
155
+ </template>
@@ -0,0 +1,80 @@
1
+ <script>
2
+ import { GlSprintf, GlAccordion, GlAccordionItem } from '@gitlab/ui';
3
+ import { translate } from '../../../../../utils/i18n';
4
+ import PreBlock from './pre_block.vue';
5
+
6
+ export default {
7
+ name: 'CreateIssueToolParams',
8
+ components: {
9
+ GlSprintf,
10
+ GlAccordion,
11
+ GlAccordionItem,
12
+ PreBlock,
13
+ },
14
+ props: {
15
+ projectId: {
16
+ type: [String, Number],
17
+ required: true,
18
+ },
19
+ projectPath: {
20
+ type: String,
21
+ required: false,
22
+ default: '',
23
+ },
24
+ title: {
25
+ type: String,
26
+ required: true,
27
+ },
28
+ description: {
29
+ type: String,
30
+ required: true,
31
+ },
32
+ labels: {
33
+ type: String,
34
+ required: false,
35
+ default: '',
36
+ },
37
+ },
38
+ computed: {
39
+ issueMessage() {
40
+ const baseMessage = this.$options.i18n.ISSUE_SUMMARY_MESSAGE_BASE;
41
+ const labelsMessage = this.$options.i18n.ISSUE_SUMMARY_MESSAGE_WITH_LABELS;
42
+
43
+ return this.labels ? `${baseMessage} ${labelsMessage}` : baseMessage;
44
+ },
45
+ },
46
+ i18n: {
47
+ ISSUE_SUMMARY_MESSAGE_BASE: translate(
48
+ 'CreateIssueToolParams.ISSUE_SUMMARY_MESSAGE_BASE',
49
+ 'Open an issue with title "%{title}" in project %{project}.'
50
+ ),
51
+ ISSUE_SUMMARY_MESSAGE_WITH_LABELS: translate(
52
+ 'CreateIssueToolParams.ISSUE_SUMMARY_MESSAGE_WITH_LABELS',
53
+ 'Assign the labels %{labels}.'
54
+ ),
55
+ ACCORDION_TITLE: translate('CreateIssueToolParams.ACCORDION_TITLE', 'Read description'),
56
+ },
57
+ };
58
+ </script>
59
+ <template>
60
+ <div class="gl-flex gl-flex-col">
61
+ <div>
62
+ <gl-sprintf :message="issueMessage">
63
+ <template #title>
64
+ <em>{{ title }}</em>
65
+ </template>
66
+ <template #project>
67
+ <code>{{ projectPath || projectId }}</code>
68
+ </template>
69
+ <template #labels>
70
+ <code>{{ labels }}</code>
71
+ </template>
72
+ </gl-sprintf>
73
+ </div>
74
+ <gl-accordion class="-gl-ml-2 gl-mt-3" :header-level="3">
75
+ <gl-accordion-item :title="$options.i18n.ACCORDION_TITLE">
76
+ <pre-block>{{ description }}</pre-block>
77
+ </gl-accordion-item>
78
+ </gl-accordion>
79
+ </div>
80
+ </template>
@@ -0,0 +1,74 @@
1
+ <script>
2
+ import { GlSprintf, GlAccordion, GlAccordionItem } from '@gitlab/ui';
3
+ import { translate } from '../../../../../utils/i18n';
4
+ import PreBlock from './pre_block.vue';
5
+
6
+ export default {
7
+ name: 'CreateMergeRequestToolParams',
8
+ components: {
9
+ GlSprintf,
10
+ GlAccordion,
11
+ GlAccordionItem,
12
+ PreBlock,
13
+ },
14
+ props: {
15
+ projectId: {
16
+ type: [String, Number],
17
+ required: true,
18
+ },
19
+ projectPath: {
20
+ type: String,
21
+ required: false,
22
+ default: '',
23
+ },
24
+ title: {
25
+ type: String,
26
+ required: true,
27
+ },
28
+ sourceBranch: {
29
+ type: String,
30
+ required: true,
31
+ },
32
+ targetBranch: {
33
+ type: String,
34
+ required: true,
35
+ },
36
+ description: {
37
+ type: String,
38
+ required: true,
39
+ },
40
+ },
41
+ i18n: {
42
+ MERGE_REQUEST_SUMMARY_MESSAGE: translate(
43
+ 'CreateMergeRequestToolParams.MERGE_REQUEST_SUMMARY_MESSAGE',
44
+ 'Open a merge request with title "%{title}" in project %{project} from branch %{sourceBranch} to branch %{targetBranch}.'
45
+ ),
46
+ ACCORDION_TITLE: translate('CreateMergeRequestToolParams.ACCORDION_TITLE', 'Read description'),
47
+ },
48
+ };
49
+ </script>
50
+ <template>
51
+ <div class="gl-flex gl-flex-col">
52
+ <div>
53
+ <gl-sprintf :message="$options.i18n.MERGE_REQUEST_SUMMARY_MESSAGE">
54
+ <template #title>
55
+ <em>{{ title }}</em>
56
+ </template>
57
+ <template #project>
58
+ <code>{{ projectPath || projectId }}</code>
59
+ </template>
60
+ <template #sourceBranch>
61
+ <code>{{ sourceBranch }}</code>
62
+ </template>
63
+ <template #targetBranch>
64
+ <code>{{ targetBranch }}</code>
65
+ </template>
66
+ </gl-sprintf>
67
+ </div>
68
+ <gl-accordion class="-gl-ml-2 gl-mt-3" :header-level="3">
69
+ <gl-accordion-item :title="$options.i18n.ACCORDION_TITLE">
70
+ <pre-block>{{ description }}</pre-block>
71
+ </gl-accordion-item>
72
+ </gl-accordion>
73
+ </div>
74
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <pre
3
+ class="gl-border gl-grid gl-text-pretty gl-rounded-lg gl-border-strong gl-bg-strong gl-p-5 gl-font-monospace"
4
+ ><code><slot></slot></code></pre>
5
+ </template>
@@ -0,0 +1,30 @@
1
+ <script>
2
+ import { GlIcon } from '@gitlab/ui';
3
+
4
+ export default {
5
+ name: 'RunCommandToolParams',
6
+ components: {
7
+ GlIcon,
8
+ },
9
+ props: {
10
+ program: {
11
+ type: String,
12
+ required: true,
13
+ },
14
+ args: {
15
+ type: String,
16
+ required: true,
17
+ },
18
+ },
19
+ computed: {
20
+ formattedCommand() {
21
+ return `${this.program} ${this.args}`;
22
+ },
23
+ },
24
+ };
25
+ </script>
26
+ <template>
27
+ <div class="gl-flex gl-items-center gl-gap-3">
28
+ <gl-icon name="terminal" /> <code>{{ formattedCommand }}</code>
29
+ </div>
30
+ </template>