@gitlab/ui 71.11.1 → 72.1.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.
- package/CHANGELOG.md +29 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.js +17 -45
- package/dist/components/experimental/duo/chat/mock_data.js +1 -9
- package/dist/tokens/css/tokens.css +1 -1
- package/dist/tokens/css/tokens.dark.css +1 -1
- package/dist/tokens/js/tokens.dark.js +1 -1
- package/dist/tokens/js/tokens.js +1 -1
- package/dist/tokens/scss/_tokens.dark.scss +1 -1
- package/dist/tokens/scss/_tokens.scss +1 -1
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +1 -1
- package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.spec.js +69 -259
- package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.vue +22 -43
- package/src/components/experimental/duo/chat/mock_data.js +0 -9
- package/src/scss/utilities.scss +16 -0
- package/src/scss/utility-mixins/flex.scss +4 -0
- package/src/scss/utility-mixins/sizing.scss +4 -0
package/package.json
CHANGED
package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.spec.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { nextTick } from 'vue';
|
|
2
2
|
import { shallowMount } from '@vue/test-utils';
|
|
3
3
|
import GlDuoUserFeedback from '../../../user_feedback/user_feedback.vue';
|
|
4
|
-
import {
|
|
5
|
-
MOCK_USER_PROMPT_MESSAGE,
|
|
6
|
-
MOCK_RESPONSE_MESSAGE,
|
|
7
|
-
MOCK_CHUNK_RESPONSE_MESSAGE,
|
|
8
|
-
} from '../../mock_data';
|
|
4
|
+
import { MOCK_USER_PROMPT_MESSAGE, MOCK_RESPONSE_MESSAGE } from '../../mock_data';
|
|
9
5
|
import DocumentationSources from '../duo_chat_message_sources/duo_chat_message_sources.vue';
|
|
10
6
|
import GlDuoChatMessage from './duo_chat_message.vue';
|
|
11
7
|
|
|
@@ -39,7 +35,7 @@ describe('DuoChatMessage', () => {
|
|
|
39
35
|
};
|
|
40
36
|
|
|
41
37
|
beforeEach(() => {
|
|
42
|
-
renderMarkdown = jest.fn().mockImplementation((val) => val);
|
|
38
|
+
renderMarkdown = jest.fn().mockImplementation((val) => `markdown: ${val}`);
|
|
43
39
|
renderGFM = jest.fn();
|
|
44
40
|
});
|
|
45
41
|
|
|
@@ -53,7 +49,7 @@ describe('DuoChatMessage', () => {
|
|
|
53
49
|
expect(customElements.get('copy-code')).toBeDefined();
|
|
54
50
|
});
|
|
55
51
|
|
|
56
|
-
describe('rendering', () => {
|
|
52
|
+
describe('rendering with user message', () => {
|
|
57
53
|
beforeEach(() => {
|
|
58
54
|
renderMarkdown.mockImplementation(() => mockMarkdownContent);
|
|
59
55
|
createComponent();
|
|
@@ -67,18 +63,16 @@ describe('DuoChatMessage', () => {
|
|
|
67
63
|
expect(wrapper.text()).toBe(mockMarkdownContent);
|
|
68
64
|
});
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
66
|
+
it('does not render the documentation sources component', () => {
|
|
67
|
+
expect(findDocumentSources().exists()).toBe(false);
|
|
68
|
+
});
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
70
|
+
it('does not render the user feedback component', () => {
|
|
71
|
+
expect(findUserFeedback().exists()).toBe(false);
|
|
78
72
|
});
|
|
79
73
|
});
|
|
80
74
|
|
|
81
|
-
describe('rendering
|
|
75
|
+
describe('rendering with assistant message', () => {
|
|
82
76
|
beforeEach(() => {
|
|
83
77
|
createComponent({
|
|
84
78
|
message: MOCK_RESPONSE_MESSAGE,
|
|
@@ -120,21 +114,16 @@ describe('DuoChatMessage', () => {
|
|
|
120
114
|
});
|
|
121
115
|
|
|
122
116
|
describe('message output', () => {
|
|
123
|
-
it('
|
|
124
|
-
createComponent();
|
|
125
|
-
await nextTick();
|
|
126
|
-
expect(renderGFM).toHaveBeenCalled();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('outputs errors if message has no content', async () => {
|
|
117
|
+
it('outputs errors if they are present', async () => {
|
|
130
118
|
const errors = ['foo', 'bar', 'baz'];
|
|
131
119
|
|
|
132
120
|
createComponent({
|
|
133
121
|
message: {
|
|
134
122
|
...MOCK_USER_PROMPT_MESSAGE,
|
|
135
|
-
contentHtml: '',
|
|
136
|
-
content: '',
|
|
137
123
|
errors,
|
|
124
|
+
contentHtml: 'fooHtml barHtml',
|
|
125
|
+
content: 'foo bar',
|
|
126
|
+
chunks: ['a', 'b', 'c'],
|
|
138
127
|
},
|
|
139
128
|
});
|
|
140
129
|
|
|
@@ -145,277 +134,98 @@ describe('DuoChatMessage', () => {
|
|
|
145
134
|
expect(findContent().text()).toContain(errors[2]);
|
|
146
135
|
});
|
|
147
136
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// reactive behavior which consistutes an exception
|
|
158
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
159
|
-
wrapper.setProps({
|
|
160
|
-
message: {
|
|
161
|
-
...MOCK_USER_PROMPT_MESSAGE,
|
|
162
|
-
contentHtml: `<p>${newContent}</p>`,
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
await nextTick();
|
|
166
|
-
expect(findContent().text()).not.toContain(MOCK_USER_PROMPT_MESSAGE.content);
|
|
167
|
-
expect(findContent().text()).toContain(newContent);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('prioritises the output of contentHtml over content', async () => {
|
|
171
|
-
// setProps is justified here because we are testing the component's
|
|
172
|
-
// reactive behavior which consistutes an exception
|
|
173
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
174
|
-
wrapper.setProps({
|
|
175
|
-
message: {
|
|
176
|
-
...MOCK_USER_PROMPT_MESSAGE,
|
|
177
|
-
contentHtml: `<p>${MOCK_USER_PROMPT_MESSAGE.content}</p>`,
|
|
178
|
-
content: newContent,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
await nextTick();
|
|
182
|
-
expect(findContent().text()).not.toContain(newContent);
|
|
183
|
-
expect(findContent().text()).toContain(MOCK_USER_PROMPT_MESSAGE.content);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('outputs errors if message has no content', async () => {
|
|
187
|
-
// setProps is justified here because we are testing the component's
|
|
188
|
-
// reactive behavior which consistutes an exception
|
|
189
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
190
|
-
wrapper.setProps({
|
|
191
|
-
message: {
|
|
192
|
-
...MOCK_USER_PROMPT_MESSAGE,
|
|
193
|
-
contentHtml: '',
|
|
194
|
-
content: '',
|
|
195
|
-
errors: ['error'],
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
await nextTick();
|
|
199
|
-
expect(findContent().text()).not.toContain(newContent);
|
|
200
|
-
expect(findContent().text()).not.toContain(MOCK_USER_PROMPT_MESSAGE.content);
|
|
201
|
-
expect(findContent().text()).toContain('error');
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('merges all the errors for output', async () => {
|
|
205
|
-
const errors = ['foo', 'bar', 'baz'];
|
|
206
|
-
// setProps is justified here because we are testing the component's
|
|
207
|
-
// reactive behavior which consistutes an exception
|
|
208
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
209
|
-
wrapper.setProps({
|
|
210
|
-
message: {
|
|
211
|
-
...MOCK_USER_PROMPT_MESSAGE,
|
|
212
|
-
contentHtml: '',
|
|
213
|
-
content: '',
|
|
214
|
-
errors,
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
await nextTick();
|
|
218
|
-
expect(findContent().text()).toContain(errors[0]);
|
|
219
|
-
expect(findContent().text()).toContain(errors[1]);
|
|
220
|
-
expect(findContent().text()).toContain(errors[2]);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('hydrates the output message with GLFM if its not a chunk', async () => {
|
|
224
|
-
// setProps is justified here because we are testing the component's
|
|
225
|
-
// reactive behavior which consistutes an exception
|
|
226
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
227
|
-
wrapper.setProps({
|
|
228
|
-
message: {
|
|
229
|
-
...MOCK_USER_PROMPT_MESSAGE,
|
|
230
|
-
contentHtml: `<p>${newContent}</p>`,
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
await nextTick();
|
|
234
|
-
expect(renderGFM).toHaveBeenCalled();
|
|
137
|
+
it('outputs contentHtml if it is present', async () => {
|
|
138
|
+
createComponent({
|
|
139
|
+
message: {
|
|
140
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
141
|
+
errors: [],
|
|
142
|
+
contentHtml: 'fooHtml barHtml',
|
|
143
|
+
content: 'foo bar',
|
|
144
|
+
chunks: ['a', 'b', 'c'],
|
|
145
|
+
},
|
|
235
146
|
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
147
|
|
|
239
|
-
|
|
240
|
-
const content1 = 'chunk #1';
|
|
241
|
-
const content2 = ' chunk #2';
|
|
242
|
-
const content3 = ' chunk #3';
|
|
243
|
-
const chunk1 = {
|
|
244
|
-
...MOCK_CHUNK_RESPONSE_MESSAGE,
|
|
245
|
-
content: content1,
|
|
246
|
-
chunkId: 1,
|
|
247
|
-
};
|
|
248
|
-
const chunk2 = {
|
|
249
|
-
...MOCK_CHUNK_RESPONSE_MESSAGE,
|
|
250
|
-
content: content2,
|
|
251
|
-
chunkId: 2,
|
|
252
|
-
};
|
|
253
|
-
const chunk3 = {
|
|
254
|
-
...MOCK_CHUNK_RESPONSE_MESSAGE,
|
|
255
|
-
content: content3,
|
|
256
|
-
chunkId: 3,
|
|
257
|
-
};
|
|
148
|
+
await nextTick();
|
|
258
149
|
|
|
259
|
-
|
|
260
|
-
createComponent();
|
|
150
|
+
expect(findContent().text()).toContain('fooHtml barHtml');
|
|
261
151
|
});
|
|
262
152
|
|
|
263
|
-
it('
|
|
264
|
-
|
|
265
|
-
// reactive behavior which consistutes an exception
|
|
266
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
267
|
-
wrapper.setProps({
|
|
153
|
+
it('outputs markdown content if there is no contentHtml', async () => {
|
|
154
|
+
createComponent({
|
|
268
155
|
message: {
|
|
269
|
-
...
|
|
270
|
-
|
|
156
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
157
|
+
errors: [],
|
|
158
|
+
contentHtml: '',
|
|
159
|
+
content: 'foo bar',
|
|
160
|
+
chunks: ['a', 'b', 'c'],
|
|
271
161
|
},
|
|
272
162
|
});
|
|
273
|
-
await nextTick();
|
|
274
|
-
expect(findContent().text()).toBe(content1);
|
|
275
|
-
});
|
|
276
163
|
|
|
277
|
-
it('renders chunks correctly when the chunks arrive out of order', async () => {
|
|
278
|
-
// setProps is justified here because we are testing the component's
|
|
279
|
-
// reactive behavior which consistutes an exception
|
|
280
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
281
|
-
wrapper.setProps({
|
|
282
|
-
message: chunk2,
|
|
283
|
-
});
|
|
284
164
|
await nextTick();
|
|
285
|
-
expect(findContent().text()).toBe('');
|
|
286
165
|
|
|
287
|
-
|
|
288
|
-
message: chunk1,
|
|
289
|
-
});
|
|
290
|
-
await nextTick();
|
|
291
|
-
expect(findContent().text()).toBe(content1 + content2);
|
|
292
|
-
|
|
293
|
-
wrapper.setProps({
|
|
294
|
-
message: chunk3,
|
|
295
|
-
});
|
|
296
|
-
await nextTick();
|
|
297
|
-
expect(findContent().text()).toBe(content1 + content2 + content3);
|
|
166
|
+
expect(findContent().text()).toContain('markdown: foo bar');
|
|
298
167
|
});
|
|
299
168
|
|
|
300
|
-
it('
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
169
|
+
it('outputs chunks if there is no content', async () => {
|
|
170
|
+
createComponent({
|
|
171
|
+
message: {
|
|
172
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
173
|
+
errors: [],
|
|
174
|
+
contentHtml: '',
|
|
175
|
+
content: '',
|
|
176
|
+
chunks: ['a', 'b', 'c'],
|
|
177
|
+
},
|
|
308
178
|
});
|
|
309
|
-
await nextTick();
|
|
310
|
-
expect(findContent().text()).toBe(content1);
|
|
311
179
|
|
|
312
|
-
wrapper.setProps({
|
|
313
|
-
message: chunk2,
|
|
314
|
-
});
|
|
315
180
|
await nextTick();
|
|
316
|
-
|
|
181
|
+
|
|
182
|
+
expect(findContent().text()).toContain('markdown: abc');
|
|
317
183
|
});
|
|
318
184
|
|
|
319
|
-
it('
|
|
185
|
+
it('outputs chunks until first undefined', async () => {
|
|
320
186
|
createComponent({
|
|
321
|
-
message:
|
|
187
|
+
message: {
|
|
188
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
189
|
+
errors: [],
|
|
190
|
+
contentHtml: '',
|
|
191
|
+
content: '',
|
|
192
|
+
chunks: ['a', undefined, 'c'],
|
|
193
|
+
},
|
|
322
194
|
});
|
|
323
|
-
await nextTick();
|
|
324
|
-
expect(findContent().text()).toBe(content1);
|
|
325
195
|
|
|
326
|
-
// setProps is justified here because we are testing the component's
|
|
327
|
-
// reactive behavior which consistutes an exception
|
|
328
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
329
|
-
wrapper.setProps({
|
|
330
|
-
message: chunk2,
|
|
331
|
-
});
|
|
332
196
|
await nextTick();
|
|
333
|
-
|
|
197
|
+
|
|
198
|
+
expect(findContent().text()).toContain('markdown: a');
|
|
334
199
|
});
|
|
335
200
|
|
|
336
|
-
it('
|
|
201
|
+
it('hydrates the message with GFM when mounting with contentHtml', async () => {
|
|
337
202
|
createComponent({
|
|
338
|
-
|
|
339
|
-
|
|
203
|
+
message: {
|
|
204
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
205
|
+
contentHtml: 'foo bar',
|
|
340
206
|
},
|
|
341
207
|
});
|
|
342
208
|
await nextTick();
|
|
343
|
-
renderGFM.
|
|
344
|
-
expect(renderGFM).not.toHaveBeenCalled();
|
|
345
|
-
|
|
346
|
-
// setProps is justified here because we are testing the component's
|
|
347
|
-
// reactive behavior which consistutes an exception
|
|
348
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
349
|
-
wrapper.setProps({
|
|
350
|
-
message: chunk2,
|
|
351
|
-
});
|
|
352
|
-
await nextTick();
|
|
353
|
-
expect(renderGFM).not.toHaveBeenCalled();
|
|
209
|
+
expect(renderGFM).toHaveBeenCalled();
|
|
354
210
|
});
|
|
355
211
|
|
|
356
|
-
it('
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
await wrapper.setProps({
|
|
363
|
-
message: chunk1,
|
|
212
|
+
it('hydrates the message with GFM when updating with contentHtml', async () => {
|
|
213
|
+
createComponent({
|
|
214
|
+
message: {
|
|
215
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
216
|
+
contentHtml: '',
|
|
217
|
+
},
|
|
364
218
|
});
|
|
365
|
-
expect(findContent().text()).toBe(content1);
|
|
366
219
|
|
|
367
|
-
|
|
220
|
+
wrapper.setProps({
|
|
368
221
|
message: {
|
|
369
|
-
...
|
|
370
|
-
|
|
371
|
-
contentHtml: finalMessageContent,
|
|
372
|
-
chunkId: null,
|
|
222
|
+
...MOCK_USER_PROMPT_MESSAGE,
|
|
223
|
+
contentHtml: 'foo bar',
|
|
373
224
|
},
|
|
374
225
|
});
|
|
375
|
-
expect(findContent().text()).toBe(finalMessageContent);
|
|
376
226
|
|
|
377
|
-
await
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
expect(findContent().text()).toBe(finalMessageContent);
|
|
227
|
+
await nextTick();
|
|
228
|
+
expect(renderGFM).toHaveBeenCalled();
|
|
381
229
|
});
|
|
382
|
-
|
|
383
|
-
it.each`
|
|
384
|
-
content | contentHtml | errors | expectedContent
|
|
385
|
-
${'alpha'} | ${'beta'} | ${['foo', 'bar']} | ${'beta'}
|
|
386
|
-
${'alpha'} | ${'beta'} | ${[]} | ${'beta'}
|
|
387
|
-
${'alpha'} | ${''} | ${['foo', 'bar']} | ${'alpha'}
|
|
388
|
-
${'alpha'} | ${''} | ${[]} | ${'alpha'}
|
|
389
|
-
${''} | ${'beta'} | ${['foo', 'bar']} | ${'beta'}
|
|
390
|
-
${''} | ${'beta'} | ${[]} | ${'beta'}
|
|
391
|
-
${''} | ${''} | ${['foo', 'bar']} | ${'foo; bar'}
|
|
392
|
-
`(
|
|
393
|
-
'outputs "$expectedContent" and hydrates this content when content is "$content", contentHtml is "$contentHtml" and errors is "$errors" with "chunkId: null"',
|
|
394
|
-
async ({ content, contentHtml, errors, expectedContent } = {}) => {
|
|
395
|
-
createComponent({
|
|
396
|
-
propsData: {
|
|
397
|
-
message: chunk1,
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
renderGFM.mockClear();
|
|
401
|
-
expect(renderGFM).not.toHaveBeenCalled();
|
|
402
|
-
|
|
403
|
-
// setProps is justified here because we are testing the component's
|
|
404
|
-
// reactive behavior which consistutes an exception
|
|
405
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
406
|
-
wrapper.setProps({
|
|
407
|
-
message: {
|
|
408
|
-
...MOCK_CHUNK_RESPONSE_MESSAGE,
|
|
409
|
-
chunkId: null,
|
|
410
|
-
content,
|
|
411
|
-
contentHtml,
|
|
412
|
-
errors,
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
await nextTick();
|
|
416
|
-
expect(renderGFM).toHaveBeenCalled();
|
|
417
|
-
expect(findContent().text()).toBe(expectedContent);
|
|
418
|
-
}
|
|
419
|
-
);
|
|
420
230
|
});
|
|
421
231
|
});
|
package/src/components/experimental/duo/chat/components/duo_chat_message/duo_chat_message.vue
CHANGED
|
@@ -5,12 +5,14 @@ import { MESSAGE_MODEL_ROLES } from '../../constants';
|
|
|
5
5
|
import DocumentationSources from '../duo_chat_message_sources/duo_chat_message_sources.vue';
|
|
6
6
|
import { CopyCodeElement } from './copy_code_element';
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
if (start === -1 || start !== 1) return ''; // If there are no non-empty elements
|
|
8
|
+
const concatUntilEmpty = (arr) => {
|
|
9
|
+
if (!arr) return '';
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
let end = arr.findIndex((el) => !el);
|
|
12
|
+
|
|
13
|
+
if (end < 0) end = arr.length;
|
|
14
|
+
|
|
15
|
+
return arr.slice(0, end).join('');
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export default {
|
|
@@ -35,12 +37,6 @@ export default {
|
|
|
35
37
|
required: true,
|
|
36
38
|
},
|
|
37
39
|
},
|
|
38
|
-
data() {
|
|
39
|
-
return {
|
|
40
|
-
messageContent: '',
|
|
41
|
-
messageWatcher: null,
|
|
42
|
-
};
|
|
43
|
-
},
|
|
44
40
|
computed: {
|
|
45
41
|
isAssistantMessage() {
|
|
46
42
|
return this.message.role.toLowerCase() === MESSAGE_MODEL_ROLES.assistant;
|
|
@@ -51,49 +47,32 @@ export default {
|
|
|
51
47
|
sources() {
|
|
52
48
|
return this.message.extras?.sources;
|
|
53
49
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.message.
|
|
57
|
-
|
|
58
|
-
)
|
|
50
|
+
messageContent() {
|
|
51
|
+
if (this.message.errors.length > 0)
|
|
52
|
+
return this.renderMarkdown(this.message.errors.join('; '));
|
|
53
|
+
|
|
54
|
+
if (this.message.contentHtml) {
|
|
55
|
+
return this.message.contentHtml;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this.renderMarkdown(this.message.content || concatUntilEmpty(this.message.chunks));
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
|
-
created() {
|
|
62
|
-
this.messageWatcher = this.$watch('message', this.messageUpdateHandler, { deep: true });
|
|
63
|
-
},
|
|
64
61
|
beforeCreate() {
|
|
65
|
-
/**
|
|
66
|
-
* Keeps cache of previous chunks used for rerendering the AI response when streaming.
|
|
67
|
-
* Is intentionally non-reactive
|
|
68
|
-
*/
|
|
69
|
-
this.messageChunks = [];
|
|
70
62
|
if (!customElements.get('copy-code')) {
|
|
71
63
|
customElements.define('copy-code', CopyCodeElement);
|
|
72
64
|
}
|
|
73
65
|
},
|
|
74
66
|
mounted() {
|
|
75
|
-
this
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.hydrateContentWithGFM();
|
|
67
|
+
this.$nextTick(this.hydrateContentWithGFM);
|
|
68
|
+
},
|
|
69
|
+
updated() {
|
|
70
|
+
this.$nextTick(this.hydrateContentWithGFM);
|
|
80
71
|
},
|
|
81
72
|
methods: {
|
|
82
73
|
async hydrateContentWithGFM() {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
async messageUpdateHandler() {
|
|
87
|
-
const { chunkId, content } = this.message;
|
|
88
|
-
if (!chunkId) {
|
|
89
|
-
this.messageChunks = [];
|
|
90
|
-
this.messageContent = this.content;
|
|
91
|
-
|
|
92
|
-
this.messageWatcher();
|
|
93
|
-
this.hydrateContentWithGFM();
|
|
94
|
-
} else {
|
|
95
|
-
this.messageChunks[chunkId] = content;
|
|
96
|
-
this.messageContent = this.renderMarkdown(concatIndicesUntilEmpty(this.messageChunks));
|
|
74
|
+
if (this.message.contentHtml) {
|
|
75
|
+
this.renderGFM(this.$refs.content);
|
|
97
76
|
}
|
|
98
77
|
},
|
|
99
78
|
},
|
|
@@ -35,15 +35,6 @@ export const MOCK_RESPONSE_MESSAGE = {
|
|
|
35
35
|
timestamp: '2021-04-21T12:00:00.000Z',
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export const MOCK_CHUNK_RESPONSE_MESSAGE = {
|
|
39
|
-
chunkId: 1,
|
|
40
|
-
content: 'chunk',
|
|
41
|
-
role: MESSAGE_MODEL_ROLES.assistant,
|
|
42
|
-
requestId: '987',
|
|
43
|
-
errors: [],
|
|
44
|
-
timestamp: '2021-04-21T12:00:00.000Z',
|
|
45
|
-
};
|
|
46
|
-
|
|
47
38
|
export const MOCK_RESPONSE_MESSAGE_FOR_STREAMING = {
|
|
48
39
|
id: '123',
|
|
49
40
|
content: `To change your password in GitLab:
|
package/src/scss/utilities.scss
CHANGED
|
@@ -3938,6 +3938,14 @@ $gl-animate-skeleton-loader-max-width: 64 * $grid-size;
|
|
|
3938
3938
|
}
|
|
3939
3939
|
}
|
|
3940
3940
|
|
|
3941
|
+
.gl-align-self-baseline {
|
|
3942
|
+
align-self: baseline;
|
|
3943
|
+
}
|
|
3944
|
+
|
|
3945
|
+
.gl-align-self-baseline\! {
|
|
3946
|
+
align-self: baseline !important;
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3941
3949
|
.gl-order-n1 {
|
|
3942
3950
|
order: -1;
|
|
3943
3951
|
}
|
|
@@ -5047,6 +5055,14 @@ $gl-animate-skeleton-loader-max-width: 64 * $grid-size;
|
|
|
5047
5055
|
height: $gl-spacing-scale-9 !important;
|
|
5048
5056
|
}
|
|
5049
5057
|
|
|
5058
|
+
.gl-h-10 {
|
|
5059
|
+
height: $gl-spacing-scale-10;
|
|
5060
|
+
}
|
|
5061
|
+
|
|
5062
|
+
.gl-h-10\! {
|
|
5063
|
+
height: $gl-spacing-scale-10 !important;
|
|
5064
|
+
}
|
|
5065
|
+
|
|
5050
5066
|
.gl-h-11 {
|
|
5051
5067
|
height: $gl-spacing-scale-11;
|
|
5052
5068
|
}
|