@gitlab/duo-ui 10.20.0 → 10.22.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 (37) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/components/agentic_chat/agentic_duo_chat.js +18 -37
  3. package/dist/components/chat/components/duo_chat_header/duo_chat_header.js +4 -4
  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/duo_chat.js +35 -54
  11. package/dist/components/chat/mock_data.js +85 -1
  12. package/dist/components/ui/duo_layout/duo_layout.js +100 -0
  13. package/dist/components/ui/side_rail/side_rail.js +67 -0
  14. package/dist/components.css +1 -1
  15. package/dist/components.css.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/tailwind.css +1 -1
  18. package/dist/tailwind.css.map +1 -1
  19. package/dist/utils/object.js +9 -0
  20. package/package.json +5 -4
  21. package/src/components/agentic_chat/agentic_duo_chat.vue +210 -244
  22. package/src/components/chat/components/duo_chat_header/duo_chat_header.vue +24 -22
  23. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_commit_tool_params.vue +155 -0
  24. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_issue_tool_params.vue +80 -0
  25. package/src/components/chat/components/duo_chat_message_tool_approval/components/create_merge_request_tool_params.vue +74 -0
  26. package/src/components/chat/components/duo_chat_message_tool_approval/components/pre_block.vue +5 -0
  27. package/src/components/chat/components/duo_chat_message_tool_approval/components/run_command_tool_params.vue +30 -0
  28. package/src/components/chat/components/duo_chat_message_tool_approval/message_tool_approval.vue +143 -88
  29. package/src/components/chat/duo_chat.scss +1 -2
  30. package/src/components/chat/duo_chat.vue +214 -238
  31. package/src/components/chat/mock_data.js +99 -0
  32. package/src/components/ui/duo_layout/duo_layout.md +0 -0
  33. package/src/components/ui/duo_layout/duo_layout.vue +95 -0
  34. package/src/components/ui/side_rail/side_rail.vue +56 -0
  35. package/src/index.js +2 -0
  36. package/src/utils/object.js +4 -0
  37. package/translations.js +29 -6
@@ -1,14 +1,24 @@
1
- import { GlButton, GlIcon, GlFormTextarea, GlFormGroup } from '@gitlab/ui';
1
+ import { GlButton, GlCard, GlFormTextarea, GlFormGroup, GlBadge } from '@gitlab/ui';
2
2
  import { translate } from '../../../../utils/i18n';
3
+ import { convertKeysToCamelCase } from '../../../../utils/object';
4
+ import CreateCommitToolParams from './components/create_commit_tool_params';
5
+ import CreateIssueToolParams from './components/create_issue_tool_params';
6
+ import CreateMergeRequestToolParams from './components/create_merge_request_tool_params';
7
+ import RunCommandToolParams from './components/run_command_tool_params';
3
8
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
4
9
 
10
+ const APPROVAL_TOOL_NAMES = {
11
+ createCommit: 'create_commit',
12
+ createIssue: 'create_issue',
13
+ createMergeRequest: 'create_merge_request',
14
+ runCommand: 'run_command'
15
+ };
5
16
  const PROCESSING_STATE = {
6
17
  APPROVING: 'approving',
7
18
  DENYING: 'denying',
8
19
  NONE: null
9
20
  };
10
21
  const i18n = {
11
- TOOL_APPROVAL_TITLE: translate('MessageToolApproval.toolApprovalTitle', 'Duo would like to execute a tool. Do you want to proceed?'),
12
22
  TOOL_APPROVAL_DESCRIPTION: translate('MessageToolApproval.toolApprovalDescription', 'GitLab Duo Agentic Chat wants to execute a tool. Do you want to proceed?'),
13
23
  TOOL_LABEL: translate('MessageToolApproval.toolLabel', 'Tool:'),
14
24
  TOOL_UNKNOWN: translate('MessageToolApproval.toolUnknown', 'Unknown'),
@@ -20,15 +30,33 @@ const i18n = {
20
30
  REQUEST_TEXT: translate('MessageToolApproval.parametersText', 'Request'),
21
31
  DENIAL_REASON_LABEL: translate('MessageToolApproval.denialReasonLabel', 'Rejection reason'),
22
32
  DENIAL_REASON_PLACEHOLDER: translate('MessageToolApproval.denialReasonPlaceholder', "Tell Duo why you're rejecting this tool execution..."),
23
- CANCEL_TEXT: translate('MessageToolApproval.cancelText', 'Cancel')
33
+ CANCEL_TEXT: translate('MessageToolApproval.cancelText', 'Cancel'),
34
+ TOOL_APPROVAL_TITLES: {
35
+ [APPROVAL_TOOL_NAMES.createCommit]: translate('MessageToolApproval.createCommit', 'Duo wants to push a commit.'),
36
+ [APPROVAL_TOOL_NAMES.createIssue]: translate('MessageToolApproval.createIssue', 'Duo wants to open an issue.'),
37
+ [APPROVAL_TOOL_NAMES.createMergeRequest]: translate('MessageToolApproval.createMergeRequest', 'Duo wants to create a merge request.'),
38
+ [APPROVAL_TOOL_NAMES.runCommand]: translate('MessageToolApproval.runCommand', 'Duo wants to run a command.')
39
+ },
40
+ TOOL_STATUS: translate('MessageToolApproval.toolStatus', 'Pending')
41
+ };
42
+ const TOOL_PARAMS_VIEW_COMPONENTS = {
43
+ [APPROVAL_TOOL_NAMES.createCommit]: 'CreateCommitToolParams',
44
+ [APPROVAL_TOOL_NAMES.createIssue]: 'CreateIssueToolParams',
45
+ [APPROVAL_TOOL_NAMES.createMergeRequest]: 'CreateMergeRequestToolParams',
46
+ [APPROVAL_TOOL_NAMES.runCommand]: 'RunCommandToolParams'
24
47
  };
25
48
  var script = {
26
49
  name: 'MessageToolApproval',
27
50
  components: {
28
51
  GlButton,
29
- GlIcon,
52
+ GlCard,
30
53
  GlFormTextarea,
31
- GlFormGroup
54
+ GlFormGroup,
55
+ GlBadge,
56
+ CreateCommitToolParams,
57
+ CreateIssueToolParams,
58
+ CreateMergeRequestToolParams,
59
+ RunCommandToolParams
32
60
  },
33
61
  props: {
34
62
  message: {
@@ -57,9 +85,15 @@ var script = {
57
85
  var _this$message2, _this$message2$tool_i;
58
86
  return ((_this$message2 = this.message) === null || _this$message2 === void 0 ? void 0 : (_this$message2$tool_i = _this$message2.tool_info) === null || _this$message2$tool_i === void 0 ? void 0 : _this$message2$tool_i.args) || {};
59
87
  },
88
+ camelCaseToolParameters() {
89
+ return convertKeysToCamelCase(this.toolParameters);
90
+ },
60
91
  hasToolParameters() {
61
92
  return Object.keys(this.toolParameters).length > 0;
62
93
  },
94
+ toolApprovalTitle() {
95
+ return i18n.TOOL_APPROVAL_TITLES[this.toolName];
96
+ },
63
97
  isApproving() {
64
98
  return this.isProcessing && this.localProcessingState === PROCESSING_STATE.APPROVING;
65
99
  },
@@ -74,6 +108,9 @@ var script = {
74
108
  },
75
109
  denyButtonText() {
76
110
  return this.isDenying ? this.$options.i18n.DENYING_TEXT : this.$options.i18n.DENY_TEXT;
111
+ },
112
+ toolParamsViewComponent() {
113
+ return TOOL_PARAMS_VIEW_COMPONENTS[this.toolName];
77
114
  }
78
115
  },
79
116
  watch: {
@@ -111,14 +148,15 @@ var script = {
111
148
  this.denialReason = '';
112
149
  }
113
150
  },
114
- i18n
151
+ i18n,
152
+ APPROVAL_TOOL_NAMES
115
153
  };
116
154
 
117
155
  /* script */
118
156
  const __vue_script__ = script;
119
157
 
120
158
  /* template */
121
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"md gl-border gl-rounded-bl-none gl-border-1 gl-border-solid gl-border-transparent gl-bg-subtle gl-p-4 gl-leading-20 gl-text-default gl-break-anywhere"},[_c('p',{staticClass:"gl-mb-3 gl-text-gray-700"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.TOOL_APPROVAL_TITLE)+"\n ")]),_vm._v(" "),_c('div',{staticClass:"gl-mb-3 gl-flex gl-items-center gl-gap-2"},[_c('gl-icon',{staticClass:"gl-text-gray-500",attrs:{"name":"work-item-maintenance"}}),_vm._v(" "),_c('strong',[_vm._v(_vm._s(_vm.toolName))])],1),_vm._v(" "),_c('div',{staticClass:"gl-border gl-mb-4 gl-rounded-base gl-border-gray-200 gl-bg-gray-50 gl-p-3"},[_c('p',{staticClass:"gl-mb-1 gl-text-sm gl-text-gray-500"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.REQUEST_TEXT)+"\n ")]),_vm._v(" "),(_vm.hasToolParameters)?_c('code',{staticClass:"gl-whitespace-pre-wrap gl-text-sm gl-text-default gl-font-monospace",attrs:{"data-testid":"tool-parameters"}},[_vm._v(_vm._s(JSON.stringify(_vm.toolParameters, null, 2)))]):_c('span',{staticClass:"gl-text-sm gl-text-gray-500",attrs:{"data-testid":"no-parameters-message"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.NO_PARAMETERS_TEXT)+"\n ")])]),_vm._v(" "),(!_vm.showDenialReason)?_c('div',{staticClass:"gl-flex gl-justify-between"},[_c('gl-button',{attrs:{"variant":"danger","size":"small","icon":"cancel","data-testid":"deny-tool-inline","disabled":_vm.buttonsDisabled,"loading":_vm.isDenying},on:{"click":_vm.handleDeny}},[_vm._v("\n "+_vm._s(_vm.denyButtonText)+"\n ")]),_vm._v(" "),_c('gl-button',{attrs:{"variant":"confirm","size":"small","icon":"play","data-testid":"approve-tool-inline","disabled":_vm.buttonsDisabled,"loading":_vm.isApproving},on:{"click":_vm.handleApprove}},[_vm._v("\n "+_vm._s(_vm.approveButtonText)+"\n ")])],1):_c('div',{staticClass:"gl-mt-3"},[_c('gl-form-group',{staticClass:"gl-mb-3",attrs:{"label":_vm.$options.i18n.DENIAL_REASON_LABEL,"label-for":"inline-rejection-reason","optional":true}},[_c('gl-form-textarea',{attrs:{"id":"inline-rejection-reason","placeholder":_vm.$options.i18n.DENIAL_REASON_PLACEHOLDER,"rows":2,"no-resize":true,"submit-on-enter":false,"disabled":_vm.buttonsDisabled,"data-testid":"denial-reason-textarea","autofocus":""},on:{"submit":_vm.submitDenial},model:{value:(_vm.denialReason),callback:function ($$v) {_vm.denialReason=$$v;},expression:"denialReason"}})],1),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-gap-3"},[_c('gl-button',{attrs:{"size":"small","data-testid":"cancel-denial","disabled":_vm.buttonsDisabled},on:{"click":_vm.cancelDenial}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CANCEL_TEXT)+"\n ")]),_vm._v(" "),_c('gl-button',{attrs:{"variant":"danger","size":"small","icon":"cancel","data-testid":"submit-denial","disabled":_vm.buttonsDisabled,"loading":_vm.isDenying},on:{"click":_vm.submitDenial}},[_vm._v("\n "+_vm._s(_vm.denyButtonText)+"\n ")])],1)],1)])};
159
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-card',{scopedSlots:_vm._u([{key:"header",fn:function(){return [_c('div',{staticClass:"gl-flex gl-items-center gl-justify-between"},[_c('span',[_vm._v("\n "+_vm._s(_vm.toolApprovalTitle)+"\n ")]),_vm._v(" "),_c('gl-badge',[_vm._v("\n "+_vm._s(_vm.$options.i18n.TOOL_STATUS)+"\n ")])],1)]},proxy:true},{key:"footer",fn:function(){return [(!_vm.showDenialReason)?_c('div',{staticClass:"gl-flex gl-gap-2"},[_c('gl-button',{attrs:{"variant":"confirm","size":"small","data-testid":"approve-tool-inline","disabled":_vm.buttonsDisabled,"loading":_vm.isApproving},on:{"click":_vm.handleApprove}},[_vm._v("\n "+_vm._s(_vm.approveButtonText)+"\n ")]),_vm._v(" "),_c('gl-button',{attrs:{"size":"small","data-testid":"deny-tool-inline","disabled":_vm.buttonsDisabled,"loading":_vm.isDenying},on:{"click":_vm.handleDeny}},[_vm._v("\n "+_vm._s(_vm.denyButtonText)+"\n ")])],1):_c('div',[_c('gl-form-group',{staticClass:"gl-mb-3",attrs:{"label":_vm.$options.i18n.DENIAL_REASON_LABEL,"label-for":"inline-rejection-reason","optional":true}},[_c('gl-form-textarea',{attrs:{"id":"inline-rejection-reason","placeholder":_vm.$options.i18n.DENIAL_REASON_PLACEHOLDER,"rows":2,"no-resize":true,"submit-on-enter":false,"disabled":_vm.buttonsDisabled,"data-testid":"denial-reason-textarea","autofocus":""},on:{"submit":_vm.submitDenial},model:{value:(_vm.denialReason),callback:function ($$v) {_vm.denialReason=$$v;},expression:"denialReason"}})],1),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-gap-3"},[_c('gl-button',{attrs:{"size":"small","data-testid":"submit-denial","variant":"confirm","disabled":_vm.buttonsDisabled,"loading":_vm.isDenying},on:{"click":_vm.submitDenial}},[_vm._v("\n "+_vm._s(_vm.denyButtonText)+"\n ")]),_vm._v(" "),_c('gl-button',{attrs:{"size":"small","data-testid":"cancel-denial","disabled":_vm.buttonsDisabled},on:{"click":_vm.cancelDenial}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CANCEL_TEXT)+"\n ")])],1)],1)]},proxy:true}])},[_vm._v(" "),(_vm.toolParamsViewComponent)?_c(_vm.toolParamsViewComponent,_vm._b({tag:"component",staticClass:"gl-leading-20"},'component',_vm.camelCaseToolParameters,false)):_c('div',{staticClass:"md gl-border gl-rounded-bl-none gl-border-1 gl-border-solid gl-border-transparent gl-bg-subtle gl-p-4 gl-leading-20 gl-text-default gl-break-anywhere"},[_c('div',{staticClass:"gl-border gl-mb-4 gl-rounded-base gl-border-gray-200 gl-bg-gray-50 gl-p-3"},[_c('p',{staticClass:"gl-mb-1 gl-text-sm gl-text-gray-500"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.REQUEST_TEXT)+"\n ")]),_vm._v(" "),(_vm.hasToolParameters)?_c('code',{staticClass:"gl-whitespace-pre-wrap gl-text-sm gl-text-default gl-font-monospace",attrs:{"data-testid":"tool-parameters"}},[_vm._v(_vm._s(JSON.stringify(_vm.toolParameters, null, 2)))]):_c('span',{staticClass:"gl-text-sm gl-text-gray-500",attrs:{"data-testid":"no-parameters-message"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.NO_PARAMETERS_TEXT)+"\n ")])])])],1)};
122
160
  var __vue_staticRenderFns__ = [];
123
161
 
124
162
  /* style */
@@ -151,4 +189,4 @@ var __vue_staticRenderFns__ = [];
151
189
  );
152
190
 
153
191
  export default __vue_component__;
154
- export { PROCESSING_STATE, i18n };
192
+ export { APPROVAL_TOOL_NAMES, PROCESSING_STATE, i18n };
@@ -1,8 +1,7 @@
1
1
  import throttle from 'lodash/throttle';
2
- import VueResizable from 'vue-resizable';
3
- import { GlButton, GlAlert, GlFormInputGroup, GlFormTextarea, GlForm, GlExperimentBadge, GlCard, GlDropdownItem, GlSafeHtmlDirective } from '@gitlab/ui';
2
+ import { GlButton, GlFormTextarea, GlForm, GlCard, GlDropdownItem, GlSafeHtmlDirective } from '@gitlab/ui';
4
3
  import { translate } from '../../utils/i18n';
5
- import { badgeTypes, badgeTypeValidator, CHAT_RESET_MESSAGE, CHAT_INCLUDE_MESSAGE, MESSAGE_MODEL_ROLES, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE } from './constants';
4
+ import { badgeTypes, badgeTypeValidator, MAX_PROMPT_LENGTH, PROMPT_LENGTH_WARNING, CHAT_RESET_MESSAGE, CHAT_INCLUDE_MESSAGE, MESSAGE_MODEL_ROLES, CHAT_CLEAR_MESSAGE, CHAT_NEW_MESSAGE } from './constants';
6
5
  import { VIEW_TYPES } from './components/duo_chat_header/constants';
7
6
  import DuoChatLoader from './components/duo_chat_loader/duo_chat_loader';
8
7
  import DuoChatPredefinedPrompts from './components/duo_chat_predefined_prompts/duo_chat_predefined_prompts';
@@ -16,10 +15,11 @@ const i18n = {
16
15
  CHAT_HISTORY_TITLE: translate('GlDuoChat.chatHistoryTitle', 'Chat history'),
17
16
  CHAT_DISCLAMER: translate('GlDuoChat.chatDisclamer', 'Responses may be inaccurate. Verify before use.'),
18
17
  CHAT_EMPTY_STATE_TITLE: translate('DuoChat.chatEmptyStateTitle', '👋 I am GitLab Duo Chat, your personal AI-powered assistant. How can I help you today?'),
19
- CHAT_PROMPT_PLACEHOLDER_DEFAULT: translate('DuoChat.chatPromptPlaceholderDefault', 'GitLab Duo Chat'),
18
+ CHAT_PROMPT_PLACEHOLDER_DEFAULT: translate('DuoChat.chatPromptPlaceholderDefault', "Let's work through this together..."),
20
19
  CHAT_PROMPT_PLACEHOLDER_WITH_COMMANDS: translate('DuoChat.chatPromptPlaceholderWithCommands', 'Type /help to learn more'),
21
20
  CHAT_SUBMIT_LABEL: translate('DuoChat.chatSubmitLabel', 'Send chat message.'),
22
21
  CHAT_CANCEL_LABEL: translate('DuoChat.chatCancelLabel', 'Cancel'),
22
+ CHAT_MODEL_PLACEHOLDER: translate('DuoChat.chatModelPlaceholder', 'GitLab Duo Chat'),
23
23
  CHAT_DEFAULT_PREDEFINED_PROMPTS: [translate('DuoChat.chatDefaultPredefinedPromptsChangePassword', 'How do I change my password in GitLab?'), translate('DuoChat.chatDefaultPredefinedPromptsForkProject', 'How do I fork a project?'), translate('DuoChat.chatDefaultPredefinedPromptsCloneRepository', 'How do I clone a repository?'), translate('DuoChat.chatDefaultPredefinedPromptsCreateTemplate', 'How do I create a template?')]
24
24
  };
25
25
  const isMessage = item => Boolean(item) && (item === null || item === void 0 ? void 0 : item.role);
@@ -45,53 +45,20 @@ var script = {
45
45
  name: 'DuoChat',
46
46
  components: {
47
47
  GlButton,
48
- GlAlert,
49
- GlFormInputGroup,
50
48
  GlFormTextarea,
51
49
  GlForm,
52
- GlExperimentBadge,
53
50
  DuoChatLoader,
54
51
  DuoChatPredefinedPrompts,
55
52
  DuoChatConversation,
56
53
  DuoChatHeader,
57
54
  DuoChatThreads,
58
55
  GlCard,
59
- GlDropdownItem,
60
- VueResizable
56
+ GlDropdownItem
61
57
  },
62
58
  directives: {
63
59
  SafeHtml: GlSafeHtmlDirective
64
60
  },
65
61
  props: {
66
- /**
67
- * Determines if the component should be resizable. When true, it renders inside
68
- * a `vue-resizable` wrapper; otherwise, a standard `div` is used.
69
- */
70
- shouldRenderResizable: {
71
- type: Boolean,
72
- required: false,
73
- default: false
74
- },
75
- /**
76
- * Defines the dimensions of the chat container when resizable.
77
- * By default, the height is set to match the height of the browser window,
78
- * and the width is fixed at 400px. The `top` position is left undefined,
79
- * allowing it to be dynamically adjusted if needed.
80
- */
81
- dimensions: {
82
- type: Object,
83
- required: false,
84
- default: () => ({
85
- width: undefined,
86
- height: undefined,
87
- top: undefined,
88
- left: undefined,
89
- maxWidth: undefined,
90
- minWidth: 400,
91
- maxHeight: undefined,
92
- minHeight: 400
93
- })
94
- },
95
62
  /**
96
63
  * The title of the chat/feature.
97
64
  */
@@ -258,11 +225,15 @@ var script = {
258
225
  required: false,
259
226
  default: () => ['en-US', 'en'],
260
227
  validator: localeValidator
228
+ },
229
+ shouldRenderResizable: {
230
+ type: Boolean,
231
+ required: false,
232
+ default: false
261
233
  }
262
234
  },
263
235
  data() {
264
236
  return {
265
- isHidden: false,
266
237
  prompt: '',
267
238
  scrolledToBottom: true,
268
239
  activeCommandIndex: 0,
@@ -270,7 +241,10 @@ var script = {
270
241
  compositionJustEnded: false,
271
242
  contextItemsMenuIsOpen: false,
272
243
  contextItemMenuRef: null,
273
- currentView: this.multiThreadedView
244
+ currentView: this.multiThreadedView,
245
+ maxPromptLength: MAX_PROMPT_LENGTH,
246
+ maxPromptLengthWarning: PROMPT_LENGTH_WARNING,
247
+ promptLengthWarningCount: MAX_PROMPT_LENGTH - PROMPT_LENGTH_WARNING
274
248
  };
275
249
  },
276
250
  computed: {
@@ -365,7 +339,6 @@ var script = {
365
339
  if (!newVal && !this.isStreaming) {
366
340
  this.displaySubmitButton = true; // Re-enable submit button when loading stops
367
341
  }
368
- this.isHidden = false;
369
342
  },
370
343
  isStreaming(newVal) {
371
344
  if (!newVal && !this.isLoading) {
@@ -404,14 +377,10 @@ var script = {
404
377
  this.focusChatInput();
405
378
  });
406
379
  },
407
- updateSize(e) {
408
- this.$emit('chat-resize', e);
409
- },
410
380
  compositionEnd() {
411
381
  this.compositionJustEnded = true;
412
382
  },
413
383
  hideChat() {
414
- this.isHidden = true;
415
384
  /**
416
385
  * Emitted when clicking the cross in the title and the chat gets closed.
417
386
  */
@@ -467,10 +436,10 @@ var script = {
467
436
  (_this$$refs$anchor = this.$refs.anchor) === null || _this$$refs$anchor === void 0 ? void 0 : (_this$$refs$anchor$sc = _this$$refs$anchor.scrollIntoView) === null || _this$$refs$anchor$sc === void 0 ? void 0 : _this$$refs$anchor$sc.call(_this$$refs$anchor);
468
437
  },
469
438
  focusChatInput() {
470
- var _this$$refs$prompt, _this$$refs$prompt$$e;
439
+ var _this$$refs$prompt, _this$$refs$prompt$$e, _this$$refs$prompt$$e2, _this$$refs$prompt$$e3;
471
440
  // This method is also called directly by consumers of this component
472
441
  // https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/dae2d4669ab4da327921492a2962beae8a05c290/webviews/vue2/gitlab_duo_chat/src/App.vue#L109
473
- (_this$$refs$prompt = this.$refs.prompt) === null || _this$$refs$prompt === void 0 ? void 0 : (_this$$refs$prompt$$e = _this$$refs$prompt.$el) === null || _this$$refs$prompt$$e === void 0 ? void 0 : _this$$refs$prompt$$e.focus();
442
+ (_this$$refs$prompt = this.$refs.prompt) === null || _this$$refs$prompt === void 0 ? void 0 : (_this$$refs$prompt$$e = _this$$refs$prompt.$el) === null || _this$$refs$prompt$$e === void 0 ? void 0 : (_this$$refs$prompt$$e2 = _this$$refs$prompt$$e.querySelector) === null || _this$$refs$prompt$$e2 === void 0 ? void 0 : (_this$$refs$prompt$$e3 = _this$$refs$prompt$$e2.call(_this$$refs$prompt$$e, 'textarea')) === null || _this$$refs$prompt$$e3 === void 0 ? void 0 : _this$$refs$prompt$$e3.focus();
474
443
  },
475
444
  onTrackFeedback(event) {
476
445
  /**
@@ -626,6 +595,12 @@ var script = {
626
595
  handleRedo(event) {
627
596
  event.preventDefault();
628
597
  document.execCommand('redo');
598
+ },
599
+ remainingCharacterCountMessage(count) {
600
+ return `${count} characters remaining`;
601
+ },
602
+ overLimitCharacterCountMessage(count) {
603
+ return `${Math.abs(count)} characters over limit`;
629
604
  }
630
605
  },
631
606
  i18n
@@ -635,13 +610,19 @@ var script = {
635
610
  const __vue_script__ = script;
636
611
 
637
612
  /* template */
638
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.shouldRenderResizable ? 'vue-resizable' : 'div',{tag:"component",class:{
639
- 'duo-chat-resizable': _vm.shouldRenderResizable,
640
- 'non-resizable-wrapper': !_vm.shouldRenderResizable,
641
- },attrs:{"width":_vm.shouldRenderResizable ? _vm.dimensions.width : null,"height":_vm.shouldRenderResizable ? _vm.dimensions.height : null,"max-width":_vm.shouldRenderResizable ? _vm.dimensions.maxWidth : null,"max-height":_vm.shouldRenderResizable ? _vm.dimensions.maxHeight : null,"min-width":_vm.shouldRenderResizable ? _vm.dimensions.minWidth : null,"left":_vm.shouldRenderResizable ? _vm.dimensions.left : null,"top":_vm.shouldRenderResizable ? _vm.dimensions.top : null,"fit-parent":true,"min-height":_vm.shouldRenderResizable ? _vm.dimensions.minHeight : null,"active":_vm.shouldRenderResizable ? ['l', 't', 'lt'] : null},on:{"resize:end":_vm.updateSize}},[(!_vm.isHidden)?_c('aside',{staticClass:"markdown-code-block duo-chat gl-bottom-0 gl-flex gl-max-h-full gl-flex-col",class:{
642
- 'resizable-content': _vm.shouldRenderResizable,
643
- 'duo-chat-drawer': !_vm.shouldRenderResizable,
644
- },attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('duo-chat-header',{ref:"header",attrs:{"active-thread-id":_vm.activeThreadId,"title":_vm.isMultithreaded && _vm.currentView === 'list' ? _vm.$options.i18n.CHAT_HISTORY_TITLE : _vm.title,"subtitle":_vm.activeThreadTitleForView,"is-multithreaded":_vm.isMultithreaded,"current-view":_vm.currentView,"should-render-resizable":_vm.shouldRenderResizable,"badge-type":_vm.isMultithreaded ? null : _vm.badgeType},on:{"go-back":_vm.onGoBack,"go-back-to-chat":_vm.onGoBackToChat,"new-chat":_vm.onNewChat,"close":_vm.hideChat},scopedSlots:_vm._u([{key:"subheader",fn:function(){return [_vm._t("subheader")]},proxy:true}],null,true)}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",attrs:{"data-testid":"chat-history"},on:{"scroll":_vm.handleScrollingThrottled}},[(_vm.shouldShowThreadList)?_c('duo-chat-threads',{attrs:{"threads":_vm.threadList,"preferred-locale":_vm.preferredLocale},on:{"new-chat":_vm.onNewChat,"select-thread":_vm.onSelectThread,"delete-thread":_vm.onDeleteThread,"close":_vm.hideChat}}):_c('transition-group',{staticClass:"duo-chat-history gl-mt-auto gl-p-5",attrs:{"mode":"out-in","tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0,"trusted-urls":_vm.trustedUrls},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"copy-code-snippet":_vm.onCopyCodeSnippet,"copy-message":_vm.onCopyMessage,"get-context-item-content":_vm.onGetContextItemContent,"open-file-path":_vm.onOpenFilePath}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state-message",staticClass:"duo-chat-message gl-rounded-bl-none gl-p-4 gl-leading-20 gl-text-gray-900 gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[(_vm.emptyStateTitle)?_c('p',{staticClass:"gl-m-0",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateTitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})],1)]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('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 && !_vm.shouldShowThreadList)?_c('footer',{staticClass:"duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0 gl-border-0 gl-bg-default gl-pb-3",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-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","data-testid":"chat-prompt-submit-button","disabled":_vm.isPromptEmpty,"aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"!gl-absolute gl-bottom-2 gl-right-2 !gl-rounded-full",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,661265825)},[_c('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-align-top",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-subtle"},[_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:{"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }if($event.shiftKey||$event.altKey||$event.metaKey){ return null; }return _vm.handleUndo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey){ return null; }return _vm.handleUndo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }if(!$event.shiftKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }if(!$event.shiftKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"y",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"y",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }return _vm.handleRedo.apply(null, arguments)}],"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),_vm._v(" "),_vm._t("footer-controls"),_vm._v(" "),_c('p',{staticClass:"gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAMER)+"\n ")])],2):_vm._e()],1):_vm._e()])};
613
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"markdown-code-block duo-chat gl-bottom-0 gl-flex gl-max-h-full gl-flex-col",attrs:{"id":"chat-component","role":"complementary","data-testid":"chat-component"}},[(_vm.showHeader)?_c('duo-chat-header',{ref:"header",attrs:{"active-thread-id":_vm.activeThreadId,"title":_vm.isMultithreaded && _vm.currentView === 'list' ? _vm.$options.i18n.CHAT_HISTORY_TITLE : _vm.title,"subtitle":_vm.activeThreadTitleForView,"is-multithreaded":_vm.isMultithreaded,"current-view":_vm.currentView,"should-render-resizable":_vm.shouldRenderResizable,"badge-type":_vm.isMultithreaded ? null : _vm.badgeType},on:{"go-back":_vm.onGoBack,"go-back-to-chat":_vm.onGoBackToChat,"new-chat":_vm.onNewChat,"close":_vm.hideChat},scopedSlots:_vm._u([{key:"subheader",fn:function(){return [_vm._t("subheader")]},proxy:true}],null,true)}):_vm._e(),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-flex-1 gl-flex-grow gl-flex-col gl-overflow-y-auto gl-overscroll-contain gl-bg-inherit",attrs:{"data-testid":"chat-history"},on:{"scroll":_vm.handleScrollingThrottled}},[(_vm.shouldShowThreadList)?_c('duo-chat-threads',{attrs:{"threads":_vm.threadList,"preferred-locale":_vm.preferredLocale},on:{"new-chat":_vm.onNewChat,"select-thread":_vm.onSelectThread,"delete-thread":_vm.onDeleteThread,"close":_vm.hideChat}}):_c('transition-group',{staticClass:"duo-chat-history gl-mt-auto gl-p-5",attrs:{"mode":"out-in","tag":"section","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('duo-chat-conversation',{key:("conversation-" + index),attrs:{"enable-code-insertion":_vm.enableCodeInsertion,"messages":conversation,"canceled-request-ids":_vm.canceledRequestIds,"show-delimiter":index > 0,"trusted-urls":_vm.trustedUrls},on:{"track-feedback":_vm.onTrackFeedback,"insert-code-snippet":_vm.onInsertCodeSnippet,"copy-code-snippet":_vm.onCopyCodeSnippet,"copy-message":_vm.onCopyMessage,"get-context-item-content":_vm.onGetContextItemContent,"open-file-path":_vm.onOpenFilePath}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state-message",staticClass:"duo-chat-message gl-rounded-bl-none gl-p-4 gl-leading-20 gl-text-gray-900 gl-break-anywhere",attrs:{"data-testid":"gl-duo-chat-empty-state"}},[(_vm.emptyStateTitle)?_c('p',{staticClass:"gl-m-0",attrs:{"data-testid":"gl-duo-chat-empty-state-title"}},[_vm._v("\n "+_vm._s(_vm.emptyStateTitle)+"\n ")]):_vm._e(),_vm._v(" "),_c('duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})],1)]:_vm._e(),_vm._v(" "),(_vm.isLoading)?_c('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 && !_vm.shouldShowThreadList)?_c('footer',{staticClass:"duo-chat-drawer-footer gl-relative gl-z-2 gl-shrink-0 gl-border-0 gl-bg-default gl-pb-3",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('div',{staticClass:"duo-chat-input gl-min-h-8 gl-max-w-full gl-grow gl-flex-col gl-rounded-bl-[12px] gl-rounded-br-[18px] gl-rounded-tl-[12px] gl-rounded-tr-[12px] gl-align-top"},[_c('div',{staticClass:"gl-flex gl-justify-between gl-border-0 gl-border-b-1 gl-border-solid gl-border-[#DCDCDE] gl-px-4 gl-py-4"},[_c('div',[_vm._v(_vm._s(_vm.$options.i18n.CHAT_MODEL_PLACEHOLDER))]),_vm._v(" "),_c('div',[_vm._t("agentic-switch")],2)]),_vm._v(" "),_c('div',{staticClass:"gl-h-[40px] gl-grow",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-subtle"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",attrs:{"data-testid":"chat-prompt-input","textarea-classes":[
614
+ '!gl-h-full',
615
+ '!gl-bg-transparent',
616
+ '!gl-py-4',
617
+ '!gl-shadow-none',
618
+ 'form-control',
619
+ 'gl-form-input',
620
+ 'gl-form-textarea',
621
+ { 'gl-truncate': !_vm.prompt } ],"placeholder":_vm.inputPlaceholder,"character-count-limit":_vm.maxPromptLength,"autofocus":""},on:{"keydown":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }if($event.shiftKey||$event.altKey||$event.metaKey){ return null; }return _vm.handleUndo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey){ return null; }return _vm.handleUndo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }if(!$event.shiftKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"z",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }if(!$event.shiftKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"y",undefined,$event.key,undefined)){ return null; }if(!$event.ctrlKey){ return null; }return _vm.handleRedo.apply(null, arguments)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"y",undefined,$event.key,undefined)){ return null; }if(!$event.metaKey){ return null; }return _vm.handleRedo.apply(null, arguments)}],"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)}},scopedSlots:_vm._u([{key:"remaining-character-count-text",fn:function(ref){
622
+ var count = ref.count;
623
+ return [(count <= _vm.promptLengthWarningCount)?_c('span',{staticClass:"gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3"},[_vm._v("\n "+_vm._s(_vm.remainingCharacterCountMessage(count))+"\n ")]):_vm._e()]}},{key:"character-count-over-limit-text",fn:function(ref){
624
+ var count = ref.count;
625
+ return [_c('span',{staticClass:"gl-absolute gl-bottom-[-25px] gl-right-px gl-pr-3"},[_vm._v(_vm._s(_vm.overLimitCharacterCountMessage(count)))])]}}],null,false,839584904),model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1),_vm._v(" "),_c('div',{staticClass:"gl-flex gl-justify-end gl-px-3 gl-pb-3"},[(_vm.displaySubmitButton)?_c('gl-button',{staticClass:"gl-bottom-2 gl-right-2 gl-ml-auto !gl-rounded-full",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","data-testid":"chat-prompt-submit-button","disabled":_vm.isPromptEmpty,"aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL}}):_c('gl-button',{staticClass:"gl-bottom-2 gl-right-2 gl-ml-auto !gl-rounded-full",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}})],1)])]),_vm._v(" "),_vm._t("footer-controls"),_vm._v(" "),_c('p',{staticClass:"gl-mb-0 gl-mt-3 gl-px-4 gl-text-sm gl-text-secondary"},[_vm._v("\n "+_vm._s(_vm.$options.i18n.CHAT_DISCLAMER)+"\n ")])],2):_vm._e()],1)};
645
626
  var __vue_staticRenderFns__ = [];
646
627
 
647
628
  /* style */
@@ -146,6 +146,90 @@ const MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_PARAMS = {
146
146
  status: 'success',
147
147
  role: 'request'
148
148
  };
149
+ const MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_MERGE_REQUEST = {
150
+ id: '125',
151
+ content: 'Tool create_merge_request requires approval. Please confirm if you want to proceed.',
152
+ message_type: MESSAGE_MODEL_ROLES.request,
153
+ tool_info: {
154
+ name: 'create_merge_request',
155
+ args: {
156
+ project_id: 123,
157
+ source_branch: 'rename-all-spec-files',
158
+ target_branch: 'main',
159
+ title: 'Rename all the *.spec.ts files to *.test.ts',
160
+ description: '## What does this MR do?\n\nThis MR updates the `group` field in the `web_ide_language_server` beta feature flag configuration from `group::remote development` to `group:: editor extensions`.\n\n## Related issues\n\nThis change aligns the feature flag with the correct group ownership.\n\n## Changes made\n\n- Updated `config/feature_flags/beta/web_ide_language_server.yml`\n- Changed group field from `group::remote development` to `group:: editor extensions`\n\n## Checklist\n\n- [x] Feature flag configuration updated\n- [x] Group field correctly set to `group:: editor extensions`'
161
+ }
162
+ },
163
+ timestamp: '2025-06-25T19:22:21.290791+00:00',
164
+ status: 'success',
165
+ role: 'request'
166
+ };
167
+ const MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_RUN_COMMAND = {
168
+ id: '125',
169
+ content: 'Tool run_command requires approval. Please confirm if you want to proceed.',
170
+ message_type: MESSAGE_MODEL_ROLES.request,
171
+ tool_info: {
172
+ name: 'run_command',
173
+ args: {
174
+ program: 'ls',
175
+ args: '-a | grep package.json'
176
+ }
177
+ },
178
+ timestamp: '2025-06-25T19:22:21.290791+00:00',
179
+ status: 'success',
180
+ role: 'request'
181
+ };
182
+ const MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_ISSUE = {
183
+ id: '125',
184
+ content: 'Tool run_command requires approval. Please confirm if you want to proceed.',
185
+ message_type: MESSAGE_MODEL_ROLES.request,
186
+ tool_info: {
187
+ name: 'create_issue',
188
+ args: {
189
+ project_id: 123,
190
+ title: 'CSP bug in VSCode Fork prevents loading external web views in Web IDE',
191
+ description: "## Problem\n\nThe VSCode Fork implementation has a Content Security Policy (CSP) bug that prevents external web views from loading properly in the Web IDE.\n\n## Impact\n\n- External web views fail to load within the Web IDE environment\n- This affects functionality that relies on embedding external content\n- Users may experience broken or non-functional web view components\n\n## Expected Behavior\n\nExternal web views should load successfully within the Web IDE when permitted by security policies.\n\n## Current Behavior\n\nExternal web views are blocked due to CSP restrictions in the VSCode Fork implementation.\n\n## Technical Details\n\nThis appears to be related to how the VSCode Fork handles Content Security Policy headers, which may be overly restrictive for the Web IDE's use case with external web views.\n\n## Next Steps\n\n- [ ] Investigate the specific CSP directives causing the issue\n- [ ] Determine which external domains/resources need to be allowlisted\n- [ ] Implement appropriate CSP modifications in the VSCode Fork\n- [ ] Test the fix with various external web view scenarios\n- [ ] Ensure security implications are properly evaluated",
192
+ labels: 'bug,web-ide,vscode-fork,csp,security'
193
+ }
194
+ },
195
+ timestamp: '2025-06-25T19:22:21.290791+00:00',
196
+ status: 'success',
197
+ role: 'request'
198
+ };
199
+ const MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_COMMIT = {
200
+ id: '125',
201
+ content: 'Tool create_commit requires approval. Please confirm if you want to proceed.',
202
+ message_type: MESSAGE_MODEL_ROLES.request,
203
+ tool_info: {
204
+ name: 'create_commit',
205
+ args: {
206
+ project_id: 35104827,
207
+ branch: 'feat/add-web-ide-duo-chat-package',
208
+ start_branch: 'main',
209
+ commit_message: 'feat: add web-ide-duo-chat package with TypeScript setup\n\n- Create new package @gitlab/web-ide-duo-chat\n- Set up TypeScript configuration following project patterns\n- Add src directory with index.ts entry point\n- Configure package.json with workspace dependencies\n- Add package reference to root tsconfig.json',
210
+ actions: [{
211
+ action: 'create',
212
+ file_path: 'packages/web-ide-duo-chat/package.json',
213
+ content: '{\n "name": "@gitlab/web-ide-duo-chat",\n "version": "0.0.1",\n "main": "./src/index.ts",\n "license": "MIT",\n "packageManager": "yarn@3.2.0",\n "devDependencies": {\n "@gitlab/utils-test": "workspace:*"\n },\n "dependencies": {\n "@gitlab/logger": "workspace:*",\n "@gitlab/web-ide-types": "workspace:*"\n },\n "publishConfig": {\n "main": "./lib/index.js"\n }\n}\n'
214
+ }, {
215
+ action: 'create',
216
+ file_path: 'packages/web-ide-duo-chat/src/types.ts',
217
+ content: "/**\n * Represents a chat message in the Duo Chat interface\n */\nexport interface ChatMessage {\n /**\n * Unique identifier for the message\n */\n id: string;\n /**\n * The content of the message\n */\n content: string;\n /**\n * Who sent the message\n */\n sender: 'user' | 'assistant';\n /**\n * Timestamp when the message was created\n */\n timestamp: Date;\n /**\n * Optional metadata for the message\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Represents a chat conversation\n */\nexport interface ChatConversation {\n /**\n * Unique identifier for the conversation\n */\n id: string;\n /**\n * Title of the conversation\n */\n title: string;\n /**\n * Messages in the conversation\n */\n messages: ChatMessage[];\n /**\n * When the conversation was created\n */\n createdAt: Date;\n /**\n * When the conversation was last updated\n */\n updatedAt: Date;\n}\n\n/**\n * Event types for Duo Chat\n */\nexport type DuoChatEvent = \n | { type: 'message-sent'; payload: ChatMessage }\n | { type: 'message-received'; payload: ChatMessage }\n | { type: 'conversation-started'; payload: ChatConversation }\n | { type: 'conversation-ended'; payload: { conversationId: string } }\n | { type: 'error'; payload: { error: Error; context?: string } };\n\n/**\n * Callback function for handling Duo Chat events\n */\nexport type DuoChatEventHandler = (event: DuoChatEvent) => void;\n\n/**\n * Interface for Duo Chat service providers\n */\nexport interface DuoChatProvider {\n /**\n * Send a message and get a response\n */\n sendMessage(message: string, conversationId?: string): Promise<ChatMessage>;\n /**\n * Start a new conversation\n */\n startConversation(title?: string): Promise<ChatConversation>;\n /**\n * Get conversation history\n */\n getConversation(conversationId: string): Promise<ChatConversation | null>;\n /**\n * List all conversations\n */\n listConversations(): Promise<ChatConversation[]>;\n}\n"
218
+ }, {
219
+ action: 'update',
220
+ file_path: 'tsconfig.json',
221
+ content: '{\n "extends": "./tsconfig.base.json",\n "references": [\n { "path": "./packages/example" },\n { "path": "./packages/gitlab-api-client" },\n { "path": "./packages/gitlab-api-client-factory" },\n { "path": "./packages/logger" },\n { "path": "./packages/oauth-client" },\n { "path": "./packages/utils-crypto" },\n { "path": "./packages/utils-escape" },\n { "path": "./packages/utils-test" },\n { "path": "./packages/utils-path" },\n { "path": "./packages/vscode-bootstrap" },\n { "path": "./packages/vscode-mediator-commands" },\n { "path": "./packages/web-ide" },\n { "path": "./packages/web-ide-duo-chat" },\n { "path": "./packages/web-ide-fs" },\n { "path": "./packages/web-ide-types" },\n { "path": "./packages/web-ide-interop" },\n { "path": "./packages/vscode-extension-web-ide" },\n { "path": "./packages/cross-origin-channel" },\n { "path": "./packages/cloudflare" }\n ],\n "files": []\n}\n'
222
+ }, {
223
+ action: 'move',
224
+ file_path: 'yarn.lock',
225
+ previous_path: 'yarn.lock.bkp'
226
+ }]
227
+ }
228
+ },
229
+ timestamp: '2025-06-25T19:22:21.290791+00:00',
230
+ status: 'success',
231
+ role: 'assistant'
232
+ };
149
233
  const MOCK_WORKFLOW_END_MESSAGE = {
150
234
  id: '123',
151
235
  content: "Search for 'duo.*chat.*message' in directory",
@@ -458,4 +542,4 @@ const AGENTIC_THREADLIST = [{
458
542
  }
459
543
  }];
460
544
 
461
- export { AGENTIC_THREADLIST, INCLUDE_SLASH_COMMAND, MOCK_AGENT_MESSAGE, MOCK_REQUEST_MESSAGE, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_PARAMS, MOCK_RESPONSE_MESSAGE, MOCK_RESPONSE_MESSAGE_FOR_STREAMING, MOCK_TOOL_MESSAGE, MOCK_TOOL_MESSAGE_WITH_LINK, MOCK_USER_PROMPT_MESSAGE, MOCK_WORKFLOW_END_MESSAGE, SLASH_COMMANDS, THREADLIST, generateMockResponseChunks, generateSeparateChunks, renderGFM, renderMarkdown };
545
+ export { AGENTIC_THREADLIST, INCLUDE_SLASH_COMMAND, MOCK_AGENT_MESSAGE, MOCK_REQUEST_MESSAGE, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_COMMIT, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_ISSUE, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_CREATE_MERGE_REQUEST, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_PARAMS, MOCK_REQUEST_MESSAGE_WITH_TOOL_APPROVAL_RUN_COMMAND, MOCK_RESPONSE_MESSAGE, MOCK_RESPONSE_MESSAGE_FOR_STREAMING, MOCK_TOOL_MESSAGE, MOCK_TOOL_MESSAGE_WITH_LINK, MOCK_USER_PROMPT_MESSAGE, MOCK_WORKFLOW_END_MESSAGE, SLASH_COMMANDS, THREADLIST, generateMockResponseChunks, generateSeparateChunks, renderGFM, renderMarkdown };
@@ -0,0 +1,100 @@
1
+ import VueResizable from 'vue-resizable';
2
+ import SideRail from '../side_rail/side_rail';
3
+ import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
4
+
5
+ var script = {
6
+ name: 'DuoLayout',
7
+ components: {
8
+ VueResizable,
9
+ SideRail
10
+ },
11
+ props: {
12
+ /**
13
+ * Determines if the component should be resizable. When true, it renders inside
14
+ * a `vue-resizable` wrapper; otherwise, a standard `div` is used.
15
+ */
16
+ shouldRenderResizable: {
17
+ type: Boolean,
18
+ required: false,
19
+ default: false
20
+ },
21
+ /**
22
+ * Defines the dimensions of the layout container when resizable.
23
+ * By default, the height is set to match the height of the browser window,
24
+ * and the width is fixed at 400px. The `top` position is left undefined,
25
+ * allowing it to be dynamically adjusted if needed.
26
+ */
27
+ dimensions: {
28
+ type: Object,
29
+ required: false,
30
+ default: () => ({
31
+ width: undefined,
32
+ height: undefined,
33
+ top: undefined,
34
+ left: undefined,
35
+ maxWidth: undefined,
36
+ minWidth: 400,
37
+ maxHeight: undefined,
38
+ minHeight: 400
39
+ })
40
+ },
41
+ /**
42
+ * Whether the layout is hidden
43
+ */
44
+ isHidden: {
45
+ type: Boolean,
46
+ required: false,
47
+ default: false
48
+ }
49
+ },
50
+ methods: {
51
+ updateSize(e) {
52
+ /**
53
+ * Emitted when the layout is resized
54
+ * @param {Object} e The resize event data
55
+ */
56
+ this.$emit('resize', e);
57
+ }
58
+ }
59
+ };
60
+
61
+ /* script */
62
+ const __vue_script__ = script;
63
+
64
+ /* template */
65
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.isHidden)?_c(_vm.shouldRenderResizable ? 'vue-resizable' : 'div',{tag:"component",class:{
66
+ 'duo-chat-resizable': _vm.shouldRenderResizable,
67
+ 'non-resizable-wrapper': !_vm.shouldRenderResizable,
68
+ },attrs:{"width":_vm.shouldRenderResizable ? _vm.dimensions.width : null,"height":_vm.shouldRenderResizable ? _vm.dimensions.height : null,"max-width":_vm.shouldRenderResizable ? _vm.dimensions.maxWidth : null,"max-height":_vm.shouldRenderResizable ? _vm.dimensions.maxHeight : null,"min-width":_vm.shouldRenderResizable ? _vm.dimensions.minWidth : null,"left":_vm.shouldRenderResizable ? _vm.dimensions.left : null,"top":_vm.shouldRenderResizable ? _vm.dimensions.top : null,"fit-parent":true,"min-height":_vm.shouldRenderResizable ? _vm.dimensions.minHeight : null,"active":_vm.shouldRenderResizable ? ['l', 't', 'lt'] : null,"data-testid":"duo-layout-component"},on:{"resize:end":_vm.updateSize}},[_c('aside',{staticClass:"markdown-code-block duo-chat gl-align-items gl-bottom-0 gl-flex gl-h-full gl-max-h-full gl-flex-row gl-bg-strong gl-py-3 gl-pl-3"},[_c('main',{staticClass:"content flex-none gl-h-full gl-min-w-0 gl-grow gl-overflow-y-auto gl-rounded-[1rem] gl-bg-neutral-0"},[_vm._t("mainview")],2),_vm._v(" "),_c('aside',{staticClass:"gl-flex gl-h-full gl-min-h-full gl-bg-strong gl-px-3 gl-py-2"},[_vm._t("siderail")],2)])]):_vm._e()};
69
+ var __vue_staticRenderFns__ = [];
70
+
71
+ /* style */
72
+ const __vue_inject_styles__ = undefined;
73
+ /* scoped */
74
+ const __vue_scope_id__ = undefined;
75
+ /* module identifier */
76
+ const __vue_module_identifier__ = undefined;
77
+ /* functional template */
78
+ const __vue_is_functional_template__ = false;
79
+ /* style inject */
80
+
81
+ /* style inject SSR */
82
+
83
+ /* style inject shadow dom */
84
+
85
+
86
+
87
+ const __vue_component__ = __vue_normalize__(
88
+ { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
89
+ __vue_inject_styles__,
90
+ __vue_script__,
91
+ __vue_scope_id__,
92
+ __vue_is_functional_template__,
93
+ __vue_module_identifier__,
94
+ false,
95
+ undefined,
96
+ undefined,
97
+ undefined
98
+ );
99
+
100
+ export default __vue_component__;
@@ -0,0 +1,67 @@
1
+ import { GlTooltipDirective, GlButtonGroup, GlButton, GlAvatar } from '@gitlab/ui';
2
+ import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
3
+
4
+ var script = {
5
+ name: 'SideRail',
6
+ directives: {
7
+ GlTooltip: GlTooltipDirective
8
+ },
9
+ components: {
10
+ GlButtonGroup,
11
+ GlButton,
12
+ GlAvatar
13
+ },
14
+ props: {
15
+ /**
16
+ * String to display the empty state of recent content
17
+ */
18
+ buttons: {
19
+ type: Object,
20
+ required: false,
21
+ default: () => ({})
22
+ }
23
+ },
24
+ methods: {
25
+ onClick(buttonName) {
26
+ this.$emit('click', buttonName);
27
+ }
28
+ }
29
+ };
30
+
31
+ /* script */
32
+ const __vue_script__ = script;
33
+
34
+ /* template */
35
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-button-group',{attrs:{"vertical":true}},[_vm._l((_vm.buttons),function(button,buttonName){return [(button.dividerBefore)?_c('div',{key:(buttonName + "-divider"),staticClass:"gl-mx-auto gl-my-3 gl-h-1 gl-w-5 gl-border-0 gl-border-t-1 gl-border-solid gl-border-[#7759C233]",attrs:{"name":"divider"}}):_vm._e(),_vm._v(" "),(button.render)?_c('gl-button',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip"}],key:(buttonName + "-button"),attrs:{"title":button.title,"category":"tertiary","variant":"default","size":"medium","icon":button.icon,"active-class":"gl-active"},on:{"click":function($event){return _vm.onClick(buttonName)}}},[(button.avatar)?_c('gl-avatar',{attrs:{"size":32,"entity-name":button.avatar,"shape":"circle"}}):_vm._e()],1):_vm._e()]})],2)};
36
+ var __vue_staticRenderFns__ = [];
37
+
38
+ /* style */
39
+ const __vue_inject_styles__ = undefined;
40
+ /* scoped */
41
+ const __vue_scope_id__ = undefined;
42
+ /* module identifier */
43
+ const __vue_module_identifier__ = undefined;
44
+ /* functional template */
45
+ const __vue_is_functional_template__ = false;
46
+ /* style inject */
47
+
48
+ /* style inject SSR */
49
+
50
+ /* style inject shadow dom */
51
+
52
+
53
+
54
+ const __vue_component__ = __vue_normalize__(
55
+ { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
56
+ __vue_inject_styles__,
57
+ __vue_script__,
58
+ __vue_scope_id__,
59
+ __vue_is_functional_template__,
60
+ __vue_module_identifier__,
61
+ false,
62
+ undefined,
63
+ undefined,
64
+ undefined
65
+ );
66
+
67
+ export default __vue_component__;