@quidgest/chatbot 0.5.3-dev.0 → 0.5.3
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/dist/components/ChatBot/ChatBot.vue.d.ts +0 -2
- package/dist/components/ChatBot/types.d.ts +4 -3
- package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +3 -3
- package/dist/components/ChatBotInput/index.d.ts +2 -2
- package/dist/components/ChatBotInput/types.d.ts +4 -4
- package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +1 -3
- package/dist/components/ChatBotMessage/__tests__/ChatBotMessage.spec.d.ts +1 -0
- package/dist/components/ChatBotMessage/types.d.ts +3 -2
- package/dist/components/FieldPreview/FieldPreview.vue.d.ts +0 -2
- package/dist/composables/useChatApi.d.ts +1 -1
- package/dist/composables/useChatMessages.d.ts +2 -2
- package/dist/composables/useTexts.d.ts +1 -4
- package/dist/index.js +25 -25
- package/dist/index.mjs +2308 -1661
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/assets/styles/preview-file.scss +70 -0
- package/src/assets/styles/styles.scss +9 -33
- package/src/components/ChatBot/ChatBot.vue +70 -71
- package/src/components/ChatBot/types.ts +4 -3
- package/src/components/ChatBotInput/ChatBotInput.vue +81 -74
- package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +29 -42
- package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +5 -5
- package/src/components/ChatBotInput/index.ts +2 -2
- package/src/components/ChatBotInput/types.ts +4 -4
- package/src/components/ChatBotMessage/ChatBotMessage.vue +34 -8
- package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +256 -0
- package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +4 -4
- package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessage.spec.ts.snap +35 -0
- package/src/components/ChatBotMessage/types.ts +4 -3
- package/src/components/ChatToolBar/ChatToolBar.vue +1 -2
- package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +18 -38
- package/src/components/FieldPreview/FieldPreview.vue +9 -31
- package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +11 -5
- package/src/components/FieldPreview/field-preview.scss +0 -4
- package/src/components/MarkdownRender/MarkdownRender.vue +0 -1
- package/src/composables/__tests__/useChatMessages.spec.ts +1 -14
- package/src/composables/useChatApi.ts +53 -57
- package/src/composables/useChatMessages.ts +4 -8
- package/src/composables/useTexts.ts +2 -5
- package/src/test/setup.ts +0 -5
|
@@ -8,19 +8,30 @@
|
|
|
8
8
|
@drop.prevent="onDrop">
|
|
9
9
|
<div class="q-chatbot__input-wrapper">
|
|
10
10
|
<div
|
|
11
|
-
v-if="
|
|
12
|
-
class="q-
|
|
11
|
+
v-if="selectedFile"
|
|
12
|
+
class="q-chatbot__file-preview">
|
|
13
13
|
<img
|
|
14
|
-
|
|
14
|
+
v-if="isImageFile"
|
|
15
|
+
class="q-chatbot__image-preview"
|
|
16
|
+
:src="filePreviewUrl"
|
|
15
17
|
tabindex="0"
|
|
16
18
|
:alt="texts.imagePreview" />
|
|
19
|
+
<div
|
|
20
|
+
v-else
|
|
21
|
+
class="q-chatbot__file-preview"
|
|
22
|
+
tabindex="0">
|
|
23
|
+
<q-icon
|
|
24
|
+
icon="file"
|
|
25
|
+
class="q-chatbot__file-icon" />
|
|
26
|
+
<span class="q-chatbot__file-name">{{ selectedFile.name }}</span>
|
|
27
|
+
</div>
|
|
17
28
|
<q-button
|
|
18
|
-
class="q-chatbot__remove-
|
|
29
|
+
class="q-chatbot__remove-file"
|
|
19
30
|
tabindex="0"
|
|
20
31
|
flat
|
|
21
32
|
round
|
|
22
|
-
@click="
|
|
23
|
-
<q-icon icon="
|
|
33
|
+
@click="removeFile">
|
|
34
|
+
<q-icon icon="remove" />
|
|
24
35
|
</q-button>
|
|
25
36
|
</div>
|
|
26
37
|
<div class="q-chatbot__input">
|
|
@@ -33,34 +44,34 @@
|
|
|
33
44
|
:disabled="props.disabled"
|
|
34
45
|
@keyup.enter="sendMessage" />
|
|
35
46
|
</div>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</
|
|
47
|
+
<div class="q-chatbot__send-container">
|
|
48
|
+
<q-button
|
|
49
|
+
:title="texts.imageUpload"
|
|
50
|
+
class="q-chatbot__upload"
|
|
51
|
+
:disabled="props.disabled || props.loading || !!selectedFile"
|
|
52
|
+
@click="triggerImageUpload">
|
|
53
|
+
<q-icon icon="upload" />
|
|
54
|
+
</q-button>
|
|
55
|
+
|
|
56
|
+
<!-- Hidden file input -->
|
|
57
|
+
<input
|
|
58
|
+
id="file-upload"
|
|
59
|
+
ref="fileInput"
|
|
60
|
+
type="file"
|
|
61
|
+
:accept="acceptedFileTypes"
|
|
62
|
+
class="hidden-input"
|
|
63
|
+
@change="handleFileUpload" />
|
|
64
|
+
|
|
65
|
+
<q-button
|
|
66
|
+
:title="texts.sendMessage"
|
|
67
|
+
variant="bold"
|
|
68
|
+
class="q-chatbot__send"
|
|
69
|
+
:disabled="isSendButtonDisabled"
|
|
70
|
+
:readonly="isSendButtonDisabled"
|
|
71
|
+
@click="sendMessage">
|
|
72
|
+
<q-icon icon="send" />
|
|
73
|
+
</q-button>
|
|
74
|
+
</div>
|
|
64
75
|
</div>
|
|
65
76
|
</div>
|
|
66
77
|
</template>
|
|
@@ -76,22 +87,28 @@
|
|
|
76
87
|
import { ref, computed } from 'vue'
|
|
77
88
|
|
|
78
89
|
// Types
|
|
79
|
-
import { ChatBotInputProps,
|
|
90
|
+
import type { ChatBotInputProps, ChatBotFile } from './'
|
|
80
91
|
|
|
81
92
|
const props = defineProps<ChatBotInputProps>()
|
|
82
93
|
|
|
83
94
|
const emit = defineEmits<{
|
|
84
|
-
|
|
95
|
+
'send-message': [prompt: string, file?: ChatBotFile]
|
|
85
96
|
}>()
|
|
86
97
|
|
|
87
98
|
const texts = useTexts()
|
|
88
99
|
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const hasSelectedImage = ref<boolean>(false)
|
|
93
|
-
|
|
100
|
+
const fileInput = ref<HTMLInputElement | null>(null)
|
|
101
|
+
const selectedFile = ref<File | null>(null)
|
|
102
|
+
const filePreviewUrl = ref<string>('')
|
|
94
103
|
const isDragging = ref(false)
|
|
104
|
+
|
|
105
|
+
const acceptedFileTypes = computed(() => '.png,.jpeg,.jpg,.svg,.webp,.pdf,.doc,.docx')
|
|
106
|
+
const isImageFile = computed(() => {
|
|
107
|
+
if (!selectedFile.value) return false
|
|
108
|
+
|
|
109
|
+
return selectedFile.value.type.startsWith('image/') ?? false
|
|
110
|
+
})
|
|
111
|
+
|
|
95
112
|
const userPrompt = ref(props.userPrompt ?? '')
|
|
96
113
|
|
|
97
114
|
const isSendButtonDisabled = computed(() => {
|
|
@@ -108,7 +125,7 @@
|
|
|
108
125
|
const chatBotFooterClasses = computed(() => {
|
|
109
126
|
return {
|
|
110
127
|
'q-chatbot__footer-disabled': props.disabled,
|
|
111
|
-
'drag-over': isDragging.value && !
|
|
128
|
+
'drag-over': isDragging.value && !selectedFile.value
|
|
112
129
|
}
|
|
113
130
|
})
|
|
114
131
|
|
|
@@ -121,22 +138,19 @@
|
|
|
121
138
|
event.preventDefault()
|
|
122
139
|
isDragging.value = false
|
|
123
140
|
|
|
124
|
-
if (props.disabled ||
|
|
141
|
+
if (props.disabled || selectedFile.value) return
|
|
125
142
|
|
|
126
143
|
const files = event.dataTransfer?.files
|
|
127
144
|
if (!files) return
|
|
145
|
+
handleFileSelection(files[0])
|
|
146
|
+
}
|
|
128
147
|
|
|
129
|
-
|
|
148
|
+
function handleFileSelection(file: File) {
|
|
149
|
+
selectedFile.value = file
|
|
130
150
|
if (file.type.startsWith('image/')) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const dataTransfer = new DataTransfer()
|
|
135
|
-
dataTransfer.items.add(file)
|
|
136
|
-
imageInput.value.files = dataTransfer.files
|
|
137
|
-
|
|
138
|
-
imagePreviewUrl.value = URL.createObjectURL(file)
|
|
139
|
-
hasSelectedImage.value = true
|
|
151
|
+
filePreviewUrl.value = URL.createObjectURL(file)
|
|
152
|
+
} else {
|
|
153
|
+
filePreviewUrl.value = ''
|
|
140
154
|
}
|
|
141
155
|
}
|
|
142
156
|
|
|
@@ -144,43 +158,36 @@
|
|
|
144
158
|
if (userPrompt.value.trim() === '' || props.loading || props.disabled) return
|
|
145
159
|
|
|
146
160
|
// Check if an image is selected
|
|
147
|
-
if (
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
file: file,
|
|
152
|
-
previewUrl: imagePreviewUrl.value
|
|
161
|
+
if (selectedFile.value) {
|
|
162
|
+
const fileData: ChatBotFile = {
|
|
163
|
+
fileData: selectedFile.value,
|
|
164
|
+
previewUrl: filePreviewUrl.value ?? ''
|
|
153
165
|
}
|
|
154
166
|
|
|
155
167
|
emit('send-message', userPrompt.value, fileData)
|
|
156
|
-
|
|
168
|
+
removeFile()
|
|
157
169
|
} else emit('send-message', userPrompt.value)
|
|
158
170
|
|
|
159
171
|
userPrompt.value = ''
|
|
160
172
|
}
|
|
161
173
|
|
|
162
|
-
function
|
|
163
|
-
|
|
174
|
+
function removeFile() {
|
|
175
|
+
selectedFile.value = null
|
|
176
|
+
filePreviewUrl.value = ''
|
|
164
177
|
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
if (fileInput.value) {
|
|
179
|
+
fileInput.value.value = ''
|
|
180
|
+
fileInput.value.files = null
|
|
168
181
|
}
|
|
169
|
-
hasSelectedImage.value = false
|
|
170
182
|
}
|
|
171
183
|
|
|
172
184
|
function triggerImageUpload() {
|
|
173
|
-
|
|
185
|
+
fileInput.value?.click()
|
|
174
186
|
}
|
|
175
187
|
|
|
176
|
-
function
|
|
177
|
-
// Store the selected image in imageInput
|
|
188
|
+
function handleFileUpload(event: Event) {
|
|
178
189
|
const target = event.target as HTMLInputElement
|
|
179
|
-
imageInput.value = target
|
|
180
190
|
|
|
181
|
-
if (target.files
|
|
182
|
-
imagePreviewUrl.value = URL.createObjectURL(target.files[0])
|
|
183
|
-
hasSelectedImage.value = true
|
|
184
|
-
}
|
|
191
|
+
if (target.files?.[0]) handleFileSelection(target.files[0])
|
|
185
192
|
}
|
|
186
193
|
</script>
|
|
@@ -7,13 +7,6 @@ import { describe, expect, it, vi } from 'vitest'
|
|
|
7
7
|
|
|
8
8
|
// Types
|
|
9
9
|
import type { ChatBotInputProps } from '..'
|
|
10
|
-
import type { ComponentPublicInstance } from 'vue'
|
|
11
|
-
|
|
12
|
-
type ChatBotInputVm = ComponentPublicInstance<{
|
|
13
|
-
imageInput: HTMLInputElement | null
|
|
14
|
-
imagePreviewUrl: string
|
|
15
|
-
hasSelectedImage: boolean
|
|
16
|
-
}>
|
|
17
10
|
|
|
18
11
|
describe('ChatBotInput', () => {
|
|
19
12
|
const props: ChatBotInputProps = {
|
|
@@ -81,7 +74,7 @@ describe('ChatBotInput', () => {
|
|
|
81
74
|
await footer.trigger('drop')
|
|
82
75
|
|
|
83
76
|
expect(wrapper.find('img').exists()).toBe(false)
|
|
84
|
-
expect(wrapper.find('.q-
|
|
77
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
|
|
85
78
|
})
|
|
86
79
|
|
|
87
80
|
it('prevents drop if theres is no files', async () => {
|
|
@@ -95,7 +88,7 @@ describe('ChatBotInput', () => {
|
|
|
95
88
|
})
|
|
96
89
|
|
|
97
90
|
expect(wrapper.find('img').exists()).toBe(false)
|
|
98
|
-
expect(wrapper.find('.q-
|
|
91
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
|
|
99
92
|
})
|
|
100
93
|
|
|
101
94
|
it('accepts image files on drop', async () => {
|
|
@@ -113,32 +106,7 @@ describe('ChatBotInput', () => {
|
|
|
113
106
|
})
|
|
114
107
|
|
|
115
108
|
expect(wrapper.find('img').exists()).toBe(true)
|
|
116
|
-
expect(wrapper.find('.q-
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('does not drop image if hidden input does not exist', async () => {
|
|
120
|
-
const wrapper = mount(ChatBotInput, {
|
|
121
|
-
props: { ...props }
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const footer = wrapper.find('.q-chatbot__footer')
|
|
125
|
-
const file = new File(['dummy content'], 'example.png', { type: 'image/png' })
|
|
126
|
-
|
|
127
|
-
// Manually set imageInput to null to simulate the missing input
|
|
128
|
-
// In reality, this should not happen as the input is always rendered but hidden
|
|
129
|
-
const vm = wrapper.vm as ChatBotInputVm
|
|
130
|
-
vm.imageInput = null
|
|
131
|
-
|
|
132
|
-
// Trigger the drop event
|
|
133
|
-
await footer.trigger('drop', {
|
|
134
|
-
dataTransfer: {
|
|
135
|
-
files: [file]
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
// Assert that no image preview is created
|
|
140
|
-
expect(wrapper.find('img').exists()).toBe(false)
|
|
141
|
-
expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(false)
|
|
109
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
|
|
142
110
|
})
|
|
143
111
|
|
|
144
112
|
it('does not send empty prompts', async () => {
|
|
@@ -222,7 +190,7 @@ describe('ChatBotInput', () => {
|
|
|
222
190
|
expect(wrapper.emitted()['send-message'][0]).toEqual([
|
|
223
191
|
'Hello',
|
|
224
192
|
{
|
|
225
|
-
|
|
193
|
+
fileData: file,
|
|
226
194
|
previewUrl: expect.stringContaining('blob:')
|
|
227
195
|
}
|
|
228
196
|
])
|
|
@@ -244,14 +212,14 @@ describe('ChatBotInput', () => {
|
|
|
244
212
|
})
|
|
245
213
|
|
|
246
214
|
expect(wrapper.find('img').exists()).toBe(true)
|
|
247
|
-
expect(wrapper.find('.q-
|
|
215
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
|
|
248
216
|
|
|
249
217
|
// Simulate clicking the remove image button
|
|
250
|
-
const removeButton = wrapper.find('.q-chatbot__remove-
|
|
218
|
+
const removeButton = wrapper.find('.q-chatbot__remove-file')
|
|
251
219
|
await removeButton.trigger('click')
|
|
252
220
|
|
|
253
221
|
expect(wrapper.find('img').exists()).toBe(false)
|
|
254
|
-
expect(wrapper.find('.q-
|
|
222
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
|
|
255
223
|
})
|
|
256
224
|
|
|
257
225
|
it('triggers click on the hidden input when upload button is clicked', async () => {
|
|
@@ -260,7 +228,7 @@ describe('ChatBotInput', () => {
|
|
|
260
228
|
})
|
|
261
229
|
|
|
262
230
|
const uploadButton = wrapper.find('.q-chatbot__upload')
|
|
263
|
-
const fileInput = wrapper.find<HTMLInputElement>('#
|
|
231
|
+
const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
|
|
264
232
|
|
|
265
233
|
// Mock the click method on the file input
|
|
266
234
|
const clickMock = vi.fn()
|
|
@@ -276,7 +244,7 @@ describe('ChatBotInput', () => {
|
|
|
276
244
|
const wrapper = mount(ChatBotInput, {
|
|
277
245
|
props: { ...props }
|
|
278
246
|
})
|
|
279
|
-
const fileInput = wrapper.find<HTMLInputElement>('#
|
|
247
|
+
const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
|
|
280
248
|
const file = new File(['dummy content'], 'example.png', { type: 'image/png' })
|
|
281
249
|
|
|
282
250
|
// Simulate selecting a file
|
|
@@ -287,6 +255,25 @@ describe('ChatBotInput', () => {
|
|
|
287
255
|
await fileInput.trigger('change')
|
|
288
256
|
|
|
289
257
|
expect(wrapper.find('img').exists()).toBe(true)
|
|
290
|
-
expect(wrapper.find('.q-
|
|
258
|
+
expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('renders a document preview for non-image files', async () => {
|
|
262
|
+
const wrapper = mount(ChatBotInput, {
|
|
263
|
+
props: { ...props }
|
|
264
|
+
})
|
|
265
|
+
const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
|
|
266
|
+
const file = new File(['dummy content'], 'example.pdf', { type: 'application/pdf' })
|
|
267
|
+
|
|
268
|
+
// Simulate selecting a file
|
|
269
|
+
const dataTransfer = new DataTransfer()
|
|
270
|
+
dataTransfer.items.add(file)
|
|
271
|
+
fileInput.element.files = dataTransfer.files
|
|
272
|
+
|
|
273
|
+
await fileInput.trigger('change')
|
|
274
|
+
|
|
275
|
+
expect(wrapper.find('img').exists()).toBe(false)
|
|
276
|
+
expect(wrapper.find('.q-chatbot__file-name').exists()).toBe(true)
|
|
277
|
+
expect(wrapper.find('.q-chatbot__file-name').text()).toBe(file.name)
|
|
291
278
|
})
|
|
292
279
|
})
|
|
@@ -15,11 +15,11 @@ exports[`ChatBotInput > renders correctly with default props 1`] = `
|
|
|
15
15
|
<!--v-if-->
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
|
+
<div class="q-chatbot__send-container"><button type="button" class="q-button q-button--outlined q-button--primary q-chatbot__upload" title="Upload Image">
|
|
19
|
+
<!--v-if--><span class="q-button__content"><span data-test="upload"></span> </span>
|
|
20
|
+
</button><!-- Hidden file input --><input id="file-upload" type="file" accept=".png,.jpeg,.jpg,.svg,.webp,.pdf,.doc,.docx" class="hidden-input"><button type="button" class="q-button q-button--bold q-button--primary q-chatbot__send" disabled="" title="Send message" readonly="">
|
|
21
|
+
<!--v-if--><span class="q-button__content"><span data-test="send"></span> </span>
|
|
22
|
+
</button></div>
|
|
18
23
|
</div>
|
|
19
|
-
<div class="q-chatbot__send-container"><button type="button" class="q-button q-button--outlined q-button--primary q-chatbot__upload" title="Upload Image">
|
|
20
|
-
<!--v-if--><span class="q-button__content"><span data-test="upload"></span> </span>
|
|
21
|
-
</button><!-- Hidden file input --><input id="image-upload" type="file" accept=".png, .jpeg, .jpg, .svg, .webp" class="hidden-input"><button type="button" class="q-button q-button--bold q-button--primary q-chatbot__send" disabled="" title="Send message" readonly="">
|
|
22
|
-
<!--v-if--><span class="q-button__content"><span data-test="send"></span> </span>
|
|
23
|
-
</button></div>
|
|
24
24
|
</div>"
|
|
25
25
|
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ChatBotInput from './ChatBotInput.vue'
|
|
2
|
-
import type { ChatBotInputProps,
|
|
2
|
+
import type { ChatBotInputProps, ChatBotFile } from './types'
|
|
3
3
|
|
|
4
4
|
export { ChatBotInput }
|
|
5
|
-
export type { ChatBotInputProps,
|
|
5
|
+
export type { ChatBotInputProps, ChatBotFile }
|
|
@@ -20,14 +20,14 @@ export type ChatBotInputProps = {
|
|
|
20
20
|
agentId?: string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export type
|
|
23
|
+
export type ChatBotFile = {
|
|
24
24
|
/**
|
|
25
|
-
* The
|
|
25
|
+
* The preview URL for the file (if it's an image)
|
|
26
26
|
*/
|
|
27
|
-
previewUrl
|
|
27
|
+
previewUrl?: string
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* The file object
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
fileData: File
|
|
33
33
|
}
|
|
@@ -8,12 +8,29 @@
|
|
|
8
8
|
|
|
9
9
|
<div class="q-chatbot__message-wrapper">
|
|
10
10
|
<div
|
|
11
|
-
v-if="
|
|
11
|
+
v-if="isImageFile"
|
|
12
12
|
class="q-chatbot__image-preview">
|
|
13
13
|
<img
|
|
14
|
-
:src="props.
|
|
14
|
+
:src="props.file?.previewUrl"
|
|
15
15
|
:alt="texts.imagePreview" />
|
|
16
16
|
</div>
|
|
17
|
+
<div
|
|
18
|
+
v-else-if="props.file?.fileData && !isImageFile"
|
|
19
|
+
class="q-chatbot__file-preview-container">
|
|
20
|
+
<div class="q-chatbot__file-icon-container">
|
|
21
|
+
<q-icon
|
|
22
|
+
icon="file"
|
|
23
|
+
class="q-chatbot__file-icon" />
|
|
24
|
+
</div>
|
|
25
|
+
<div class="q-chatbot__file-info">
|
|
26
|
+
<span class="q-chatbot__file-name">
|
|
27
|
+
{{ props.file?.fileData.name }}
|
|
28
|
+
</span>
|
|
29
|
+
<span class="q-chatbot__file-extension">
|
|
30
|
+
{{ fileExtension }}
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
17
34
|
<div class="q-chatbot__message">
|
|
18
35
|
<pulse-dots v-if="loading" />
|
|
19
36
|
<template v-else-if="props.sender === 'bot' && props.fields.length > 0">
|
|
@@ -25,7 +42,6 @@
|
|
|
25
42
|
:name="field.name"
|
|
26
43
|
:type="field.type"
|
|
27
44
|
:disabled="loading"
|
|
28
|
-
@regenerate="() => emit('regenerate', field.name)"
|
|
29
45
|
@apply="(text) => applyField(text, field)" />
|
|
30
46
|
</template>
|
|
31
47
|
<template v-else>
|
|
@@ -35,7 +51,7 @@
|
|
|
35
51
|
:source="props.message || ''" />
|
|
36
52
|
<div
|
|
37
53
|
v-else
|
|
38
|
-
class="q-chatbot__text">
|
|
54
|
+
class="q-chatbot__text q-chatbot__user-text">
|
|
39
55
|
{{ props.message }}
|
|
40
56
|
</div>
|
|
41
57
|
</template>
|
|
@@ -58,8 +74,8 @@
|
|
|
58
74
|
import { QIcon } from '@quidgest/ui/components'
|
|
59
75
|
|
|
60
76
|
// Types
|
|
77
|
+
import type { ChatBotMessageProps } from './'
|
|
61
78
|
import { AppliedFieldData, FieldData } from '../ChatBot/types'
|
|
62
|
-
import type { ChatBotMessageProps } from './types'
|
|
63
79
|
|
|
64
80
|
// Composables
|
|
65
81
|
import { useTexts } from '@/composables/useTexts'
|
|
@@ -81,7 +97,6 @@
|
|
|
81
97
|
|
|
82
98
|
const emit = defineEmits<{
|
|
83
99
|
(e: 'apply-fields', fields: AppliedFieldData[]): void
|
|
84
|
-
(e: 'regenerate', fieldName: string): void
|
|
85
100
|
}>()
|
|
86
101
|
|
|
87
102
|
const texts = useTexts()
|
|
@@ -96,10 +111,21 @@
|
|
|
96
111
|
)
|
|
97
112
|
})
|
|
98
113
|
|
|
114
|
+
const isImageFile = computed(() => {
|
|
115
|
+
return props.file?.fileData.type.startsWith('image/') ?? false
|
|
116
|
+
})
|
|
117
|
+
|
|
99
118
|
const messageImage = computed(() =>
|
|
100
119
|
props.sender === 'bot' ? props.chatbotImage : props.userImage
|
|
101
120
|
)
|
|
102
121
|
|
|
122
|
+
const fileExtension = computed(() => {
|
|
123
|
+
if (!props.file?.fileData) return ''
|
|
124
|
+
const ext = props.file.fileData.name.split('.').pop()?.toUpperCase()
|
|
125
|
+
|
|
126
|
+
return ext ?? ''
|
|
127
|
+
})
|
|
128
|
+
|
|
103
129
|
function copyResponse() {
|
|
104
130
|
if (!props.message) return
|
|
105
131
|
|
|
@@ -114,7 +140,7 @@
|
|
|
114
140
|
}
|
|
115
141
|
|
|
116
142
|
function applyField(text: unknown, field: FieldData) {
|
|
117
|
-
emit('apply-fields', [{
|
|
143
|
+
emit('apply-fields', [{ name: field.name, text }])
|
|
118
144
|
}
|
|
119
145
|
|
|
120
146
|
function applyAllFields() {
|
|
@@ -122,7 +148,7 @@
|
|
|
122
148
|
|
|
123
149
|
const fieldsToApply = props.fields.map((field) => {
|
|
124
150
|
return {
|
|
125
|
-
|
|
151
|
+
name: field.name,
|
|
126
152
|
text: parseFieldValue(field.type, field.text)
|
|
127
153
|
}
|
|
128
154
|
})
|