@quidgest/chatbot 0.5.1 → 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.
Files changed (70) hide show
  1. package/README.md +1 -2
  2. package/dist/components/ChatBot/types.d.ts +3 -1
  3. package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +3 -3
  4. package/dist/components/ChatBotInput/__tests__/ChatBotInput.spec.d.ts +1 -0
  5. package/dist/components/ChatBotInput/index.d.ts +2 -2
  6. package/dist/components/ChatBotInput/types.d.ts +4 -4
  7. package/dist/components/ChatBotMessage/__tests__/ChatBotMessage.spec.d.ts +1 -0
  8. package/dist/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.d.ts +1 -0
  9. package/dist/components/ChatBotMessage/types.d.ts +3 -2
  10. package/dist/components/ChatToolBar/__tests__/ChatToolBar.spec.d.ts +1 -0
  11. package/dist/components/FieldPreview/__tests__/FieldPreview.spec.d.ts +1 -0
  12. package/dist/components/MarkdownRender/__tests__/MarkdownRender.spec.d.ts +1 -0
  13. package/dist/components/PulseDots/__tests__/PulseDots.spec.d.ts +1 -0
  14. package/dist/composables/__tests__/useChatMessages.spec.d.ts +1 -0
  15. package/dist/composables/__tests__/useSSE.spec.d.ts +1 -0
  16. package/dist/composables/useChatMessages.d.ts +2 -1
  17. package/dist/composables/useSSE.d.ts +1 -2
  18. package/dist/composables/useTexts.d.ts +2 -0
  19. package/dist/index.js +16 -16
  20. package/dist/index.mjs +2924 -1770
  21. package/dist/style.css +1 -1
  22. package/dist/test/setup.d.ts +1 -0
  23. package/dist/utils/__tests__/parseFieldValue.spec.d.ts +1 -0
  24. package/package.json +27 -5
  25. package/src/assets/styles/preview-file.scss +70 -0
  26. package/src/assets/styles/styles.scss +190 -222
  27. package/src/components/ChatBot/ChatBot.vue +345 -368
  28. package/src/components/ChatBot/types.ts +35 -33
  29. package/src/components/ChatBotInput/ChatBotInput.vue +188 -190
  30. package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +279 -0
  31. package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +25 -0
  32. package/src/components/ChatBotInput/index.ts +2 -2
  33. package/src/components/ChatBotInput/types.ts +25 -25
  34. package/src/components/ChatBotMessage/ChatBotMessage.vue +159 -134
  35. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +179 -164
  36. package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +256 -0
  37. package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +199 -0
  38. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessage.spec.ts.snap +35 -0
  39. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessageButtons.spec.ts.snap +25 -0
  40. package/src/components/ChatBotMessage/types.ts +54 -53
  41. package/src/components/ChatToolBar/ChatToolBar.vue +68 -64
  42. package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +118 -0
  43. package/src/components/ChatToolBar/__tests__/__snapshots__/ChatToolBar.spec.ts.snap +11 -0
  44. package/src/components/ChatToolBar/types.ts +12 -12
  45. package/src/components/FieldPreview/FieldPreview.vue +56 -58
  46. package/src/components/FieldPreview/__tests__/FieldPreview.spec.ts +72 -0
  47. package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +25 -0
  48. package/src/components/FieldPreview/field-preview.scss +26 -26
  49. package/src/components/FieldPreview/types.ts +5 -5
  50. package/src/components/MarkdownRender/MarkdownRender.vue +15 -15
  51. package/src/components/MarkdownRender/__tests__/MarkdownRender.spec.ts +68 -0
  52. package/src/components/MarkdownRender/__tests__/__snapshots__/MarkdownRender.spec.ts.snap +8 -0
  53. package/src/components/MarkdownRender/markdown-render.scss +19 -20
  54. package/src/components/MarkdownRender/types.ts +3 -3
  55. package/src/components/PulseDots/PulseDots.vue +17 -17
  56. package/src/components/PulseDots/__tests__/PulseDots.spec.ts +35 -0
  57. package/src/components/PulseDots/__tests__/__snapshots__/PulseDots.spec.ts.snap +7 -0
  58. package/src/components/PulseDots/__tests__/__snapshots__/pulse-dots.spec.ts.snap +7 -0
  59. package/src/components/PulseDots/pulse-dots.scss +24 -23
  60. package/src/composables/__tests__/useChatMessages.spec.ts +51 -0
  61. package/src/composables/__tests__/useSSE.spec.ts +132 -0
  62. package/src/composables/useChatApi.ts +128 -134
  63. package/src/composables/useChatMessages.ts +46 -48
  64. package/src/composables/useSSE.ts +75 -76
  65. package/src/composables/useTexts.ts +30 -30
  66. package/src/test/setup.ts +36 -0
  67. package/src/utils/__tests__/parseFieldValue.spec.ts +27 -0
  68. package/src/utils/parseFieldValue.ts +12 -0
  69. package/src/utils/helper.ts +0 -12
  70. /package/dist/utils/{helper.d.ts → parseFieldValue.d.ts} +0 -0
@@ -1,55 +1,57 @@
1
+ import { ChatBotFile } from '../ChatBotInput'
2
+
1
3
  export type ChatBotProps = {
2
- apiEndpoint?: string
3
- controllerEndpoint?: string
4
- username: string
5
- projectPath: string
6
- userImage?: string
7
- chatbotImage?: string
8
- dateFormat: string
9
- agentData?: AgentData
10
- availableAgents?: AvailableAgents[]
4
+ apiEndpoint?: string
5
+ controllerEndpoint?: string
6
+ username: string
7
+ projectPath: string
8
+ userImage?: string
9
+ chatbotImage?: string
10
+ dateFormat: string
11
+ agentData?: AgentData
12
+ availableAgents?: AvailableAgents[]
11
13
  }
12
14
 
13
15
  export type AvailableAgents = {
14
- key: string
15
- value: string
16
- formId: string
16
+ key: string
17
+ value: string
18
+ formId: string
17
19
  }
18
20
 
19
21
  export type AgentData = {
20
- id: string
21
- jobId: string
22
- formId: string
22
+ id: string
23
+ jobId: string
24
+ formId: string
23
25
  }
24
26
 
25
27
  export type FieldData = {
26
- name: string
27
- type: string
28
- text: string
29
- applied?: boolean
28
+ name: string
29
+ type: string
30
+ text: string
31
+ applied?: boolean
30
32
  }
31
33
 
32
34
  export type AppliedFieldData = {
33
- name: string
34
- text: unknown
35
+ name: string
36
+ text: unknown
35
37
  }
36
38
 
37
39
  export type ChatMessage = {
38
- id: number
39
- message: string
40
- date: Date
41
- sender: ChatBotMessageSender
42
- sessionID: string
43
- imagePreviewUrl?: string
44
- isWelcomeMessage?: boolean
45
- fields?: FieldData[]
40
+ id: number
41
+ message: string
42
+ date: Date
43
+ sender: ChatBotMessageSender
44
+ sessionID: string
45
+ file?: ChatBotFile
46
+ isWelcomeMessage?: boolean
47
+ fields?: FieldData[]
46
48
  }
47
49
 
48
50
  export type ChatBotMessageContent = {
49
- content: string
50
- type: string
51
- sessionID: string
52
- imageUrl?: string
51
+ content: string
52
+ type: string
53
+ sessionID: string
54
+ imageUrl?: string
53
55
  }
54
56
 
55
57
  export type ChatBotMessageSender = 'bot' | 'user'
@@ -1,195 +1,193 @@
1
1
  <template>
2
- <q-label :label="texts.inputLabel" />
3
- <div
4
- class="q-chatbot__footer"
5
- @dragover.prevent="onDragOver"
6
- @dragleave.prevent="onDragLeave"
7
- @drop.prevent="onDrop"
8
- :class="chatBotFooterClasses">
9
- <div class="q-chatbot__input-wrapper">
10
- <div
11
- v-if="imagePreviewUrl"
12
- class="q-chatbot__image-preview">
13
- <img
14
- :src="imagePreviewUrl"
15
- tabindex="0"
16
- alt="Image preview" />
17
- <q-button
18
- class="q-chatbot__remove-image"
19
- tabindex="0"
20
- flat
21
- round
22
- @click="removeImage">
23
- <q-icon icon="bin" />
24
- </q-button>
25
- </div>
26
- <div class="q-chatbot__input">
27
- <q-text-area
28
- v-model="userPrompt"
29
- size="block"
30
- autosize
31
- resize="none"
32
- :rows="2"
33
- :disabled="props.disabled"
34
- @keyup.enter="sendMessage" />
35
- </div>
36
- </div>
37
- <div class="q-chatbot__send-container">
38
- <q-button
39
- :title="texts.imageUpload"
40
- class="q-chatbot__upload"
41
- :disabled="props.disabled || props.loading || hasSelectedImage"
42
- @click="triggerImageUpload">
43
- <q-icon icon="upload" />
44
- </q-button>
45
-
46
- <!-- Hidden file input -->
47
- <input
48
- id="image-upload"
49
- type="file"
50
- ref="imageInput"
51
- @change="handleImageUpload"
52
- :accept="acceptedImageTypes"
53
- class="hidden-input" />
54
-
55
- <q-button
56
- :title="texts.sendMessage"
57
- variant="bold"
58
- class="q-chatbot__send"
59
- :disabled="isSendButtonDisabled"
60
- :readonly="isSendButtonDisabled"
61
- @click="sendMessage">
62
- <q-icon icon="send" />
63
- </q-button>
64
- </div>
65
- </div>
2
+ <q-label :label="texts.inputLabel" />
3
+ <div
4
+ class="q-chatbot__footer"
5
+ :class="chatBotFooterClasses"
6
+ @dragover.prevent="onDragOver"
7
+ @dragleave.prevent="onDragLeave"
8
+ @drop.prevent="onDrop">
9
+ <div class="q-chatbot__input-wrapper">
10
+ <div
11
+ v-if="selectedFile"
12
+ class="q-chatbot__file-preview">
13
+ <img
14
+ v-if="isImageFile"
15
+ class="q-chatbot__image-preview"
16
+ :src="filePreviewUrl"
17
+ tabindex="0"
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>
28
+ <q-button
29
+ class="q-chatbot__remove-file"
30
+ tabindex="0"
31
+ flat
32
+ round
33
+ @click="removeFile">
34
+ <q-icon icon="remove" />
35
+ </q-button>
36
+ </div>
37
+ <div class="q-chatbot__input">
38
+ <q-text-area
39
+ v-model="userPrompt"
40
+ size="block"
41
+ autosize
42
+ resize="none"
43
+ :rows="2"
44
+ :disabled="props.disabled"
45
+ @keyup.enter="sendMessage" />
46
+ </div>
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>
75
+ </div>
76
+ </div>
66
77
  </template>
67
78
 
68
79
  <script setup lang="ts">
69
- // Components
70
- import { QLabel } from '@quidgest/ui/components'
71
-
72
- // Composables
73
- import { useTexts } from '@/composables/useTexts'
74
-
75
- // Utils
76
- import { ref, computed } from 'vue'
77
-
78
- // Types
79
- import { ChatBotInputProps, ChatBotImage } from './types'
80
-
81
- const texts = useTexts()
82
-
83
- const props = defineProps<ChatBotInputProps>()
84
-
85
- const imageInput = ref<HTMLInputElement | null>(null)
86
- const imagePreviewUrl = ref<string>('')
87
- const acceptedImageTypes = computed(() => '.png, .jpeg, .jpg, .svg, .webp')
88
- const hasSelectedImage = ref<boolean>(false)
89
-
90
- const isDragging = ref(false)
91
- const userPrompt = ref(props.userPrompt ?? '')
92
-
93
- const emits = defineEmits<{
94
- (e: 'sendMessage', prompt: string, image?: ChatBotImage): void
95
- }>()
96
-
97
- const isSendButtonDisabled = computed(() => {
98
- return (
99
- props.disabled ||
100
- props.loading ||
101
- userPrompt.value.trim().length === 0
102
- )
103
- })
104
-
105
- function onDragOver(event: DragEvent) {
106
- event.preventDefault()
107
- if (!props.disabled) {
108
- isDragging.value = true
109
- }
110
- }
111
-
112
- const chatBotFooterClasses = computed(() => {
113
- return {
114
- 'q-chatbot__footer-disabled': props.disabled,
115
- 'drag-over': isDragging.value && !hasSelectedImage.value
116
- }
117
- })
118
-
119
- function onDragLeave(event: DragEvent) {
120
- event.preventDefault()
121
- isDragging.value = false
122
- }
123
-
124
- function onDrop(event: DragEvent) {
125
- event.preventDefault()
126
- isDragging.value = false
127
-
128
- if (props.disabled || hasSelectedImage.value) return
129
-
130
- const files = event.dataTransfer?.files
131
- if (!files) return
132
-
133
- const file = files[0]
134
- if (file.type.startsWith('image/')) {
135
- if (!imageInput.value) return
136
-
137
- // Create a new FileList-like object
138
- const dataTransfer = new DataTransfer()
139
- dataTransfer.items.add(file)
140
- imageInput.value.files = dataTransfer.files
141
-
142
- imagePreviewUrl.value = URL.createObjectURL(file)
143
- hasSelectedImage.value = true
144
- }
145
- }
146
-
147
- function sendMessage() {
148
- if (userPrompt.value.trim() === '' || props.loading || props.disabled)
149
- return
150
-
151
- // Check if an image is selected
152
- if (
153
- imageInput.value &&
154
- imageInput.value.files &&
155
- imageInput.value.files.length > 0
156
- ) {
157
- const file = imageInput.value.files[0]
158
-
159
- const fileData: ChatBotImage = {
160
- file: file,
161
- previewUrl: imagePreviewUrl.value
162
- }
163
-
164
- emits('sendMessage', userPrompt.value, fileData)
165
- removeImage()
166
- } else emits('sendMessage', userPrompt.value)
167
-
168
- userPrompt.value = ''
169
- }
170
-
171
- function removeImage() {
172
- imagePreviewUrl.value = ''
173
-
174
- if (imageInput.value) {
175
- imageInput.value.value = ''
176
- imageInput.value.files = null
177
- }
178
- hasSelectedImage.value = false
179
- }
180
-
181
- function triggerImageUpload() {
182
- imageInput.value?.click()
183
- }
184
-
185
- function handleImageUpload(event: Event) {
186
- // Store the selected image in imageInput
187
- const target = event.target as HTMLInputElement
188
- imageInput.value = target
189
-
190
- if (target.files && target.files[0]) {
191
- imagePreviewUrl.value = URL.createObjectURL(target.files[0])
192
- hasSelectedImage.value = true
193
- }
194
- }
80
+ // Components
81
+ import { QLabel, QButton, QTextArea, QIcon } from '@quidgest/ui/components'
82
+
83
+ // Composables
84
+ import { useTexts } from '@/composables/useTexts'
85
+
86
+ // Utils
87
+ import { ref, computed } from 'vue'
88
+
89
+ // Types
90
+ import type { ChatBotInputProps, ChatBotFile } from './'
91
+
92
+ const props = defineProps<ChatBotInputProps>()
93
+
94
+ const emit = defineEmits<{
95
+ 'send-message': [prompt: string, file?: ChatBotFile]
96
+ }>()
97
+
98
+ const texts = useTexts()
99
+
100
+ const fileInput = ref<HTMLInputElement | null>(null)
101
+ const selectedFile = ref<File | null>(null)
102
+ const filePreviewUrl = ref<string>('')
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
+
112
+ const userPrompt = ref(props.userPrompt ?? '')
113
+
114
+ const isSendButtonDisabled = computed(() => {
115
+ return props.disabled || props.loading || userPrompt.value.trim().length === 0
116
+ })
117
+
118
+ function onDragOver(event: DragEvent) {
119
+ event.preventDefault()
120
+ if (!props.disabled) {
121
+ isDragging.value = true
122
+ }
123
+ }
124
+
125
+ const chatBotFooterClasses = computed(() => {
126
+ return {
127
+ 'q-chatbot__footer-disabled': props.disabled,
128
+ 'drag-over': isDragging.value && !selectedFile.value
129
+ }
130
+ })
131
+
132
+ function onDragLeave(event: DragEvent) {
133
+ event.preventDefault()
134
+ isDragging.value = false
135
+ }
136
+
137
+ function onDrop(event: DragEvent) {
138
+ event.preventDefault()
139
+ isDragging.value = false
140
+
141
+ if (props.disabled || selectedFile.value) return
142
+
143
+ const files = event.dataTransfer?.files
144
+ if (!files) return
145
+ handleFileSelection(files[0])
146
+ }
147
+
148
+ function handleFileSelection(file: File) {
149
+ selectedFile.value = file
150
+ if (file.type.startsWith('image/')) {
151
+ filePreviewUrl.value = URL.createObjectURL(file)
152
+ } else {
153
+ filePreviewUrl.value = ''
154
+ }
155
+ }
156
+
157
+ function sendMessage() {
158
+ if (userPrompt.value.trim() === '' || props.loading || props.disabled) return
159
+
160
+ // Check if an image is selected
161
+ if (selectedFile.value) {
162
+ const fileData: ChatBotFile = {
163
+ fileData: selectedFile.value,
164
+ previewUrl: filePreviewUrl.value ?? ''
165
+ }
166
+
167
+ emit('send-message', userPrompt.value, fileData)
168
+ removeFile()
169
+ } else emit('send-message', userPrompt.value)
170
+
171
+ userPrompt.value = ''
172
+ }
173
+
174
+ function removeFile() {
175
+ selectedFile.value = null
176
+ filePreviewUrl.value = ''
177
+
178
+ if (fileInput.value) {
179
+ fileInput.value.value = ''
180
+ fileInput.value.files = null
181
+ }
182
+ }
183
+
184
+ function triggerImageUpload() {
185
+ fileInput.value?.click()
186
+ }
187
+
188
+ function handleFileUpload(event: Event) {
189
+ const target = event.target as HTMLInputElement
190
+
191
+ if (target.files?.[0]) handleFileSelection(target.files[0])
192
+ }
195
193
  </script>