@quidgest/chatbot 0.5.1 → 0.5.3-dev.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 (66) hide show
  1. package/README.md +1 -2
  2. package/dist/components/ChatBot/ChatBot.vue.d.ts +2 -0
  3. package/dist/components/ChatBot/types.d.ts +2 -1
  4. package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +2 -2
  5. package/dist/components/ChatBotInput/__tests__/ChatBotInput.spec.d.ts +1 -0
  6. package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +3 -1
  7. package/dist/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.d.ts +1 -0
  8. package/dist/components/ChatToolBar/__tests__/ChatToolBar.spec.d.ts +1 -0
  9. package/dist/components/FieldPreview/FieldPreview.vue.d.ts +2 -0
  10. package/dist/components/FieldPreview/__tests__/FieldPreview.spec.d.ts +1 -0
  11. package/dist/components/MarkdownRender/__tests__/MarkdownRender.spec.d.ts +1 -0
  12. package/dist/components/PulseDots/__tests__/PulseDots.spec.d.ts +1 -0
  13. package/dist/composables/__tests__/useChatMessages.spec.d.ts +1 -0
  14. package/dist/composables/__tests__/useSSE.spec.d.ts +1 -0
  15. package/dist/composables/useChatApi.d.ts +1 -1
  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 +5 -0
  19. package/dist/index.js +25 -25
  20. package/dist/index.mjs +2319 -1812
  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 -6
  25. package/src/assets/styles/styles.scss +212 -220
  26. package/src/components/ChatBot/ChatBot.vue +346 -368
  27. package/src/components/ChatBot/types.ts +34 -33
  28. package/src/components/ChatBotInput/ChatBotInput.vue +181 -190
  29. package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +292 -0
  30. package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +25 -0
  31. package/src/components/ChatBotInput/types.ts +24 -24
  32. package/src/components/ChatBotMessage/ChatBotMessage.vue +133 -134
  33. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +179 -164
  34. package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +199 -0
  35. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessageButtons.spec.ts.snap +25 -0
  36. package/src/components/ChatBotMessage/types.ts +52 -52
  37. package/src/components/ChatToolBar/ChatToolBar.vue +69 -64
  38. package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +138 -0
  39. package/src/components/ChatToolBar/__tests__/__snapshots__/ChatToolBar.spec.ts.snap +11 -0
  40. package/src/components/ChatToolBar/types.ts +12 -12
  41. package/src/components/FieldPreview/FieldPreview.vue +83 -63
  42. package/src/components/FieldPreview/__tests__/FieldPreview.spec.ts +72 -0
  43. package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +19 -0
  44. package/src/components/FieldPreview/field-preview.scss +28 -24
  45. package/src/components/FieldPreview/types.ts +5 -5
  46. package/src/components/MarkdownRender/MarkdownRender.vue +16 -15
  47. package/src/components/MarkdownRender/__tests__/MarkdownRender.spec.ts +68 -0
  48. package/src/components/MarkdownRender/__tests__/__snapshots__/MarkdownRender.spec.ts.snap +8 -0
  49. package/src/components/MarkdownRender/markdown-render.scss +19 -20
  50. package/src/components/MarkdownRender/types.ts +3 -3
  51. package/src/components/PulseDots/PulseDots.vue +17 -17
  52. package/src/components/PulseDots/__tests__/PulseDots.spec.ts +35 -0
  53. package/src/components/PulseDots/__tests__/__snapshots__/PulseDots.spec.ts.snap +7 -0
  54. package/src/components/PulseDots/__tests__/__snapshots__/pulse-dots.spec.ts.snap +7 -0
  55. package/src/components/PulseDots/pulse-dots.scss +24 -23
  56. package/src/composables/__tests__/useChatMessages.spec.ts +64 -0
  57. package/src/composables/__tests__/useSSE.spec.ts +132 -0
  58. package/src/composables/useChatApi.ts +132 -134
  59. package/src/composables/useChatMessages.ts +50 -48
  60. package/src/composables/useSSE.ts +75 -76
  61. package/src/composables/useTexts.ts +33 -30
  62. package/src/test/setup.ts +41 -0
  63. package/src/utils/__tests__/parseFieldValue.spec.ts +27 -0
  64. package/src/utils/parseFieldValue.ts +12 -0
  65. package/src/utils/helper.ts +0 -12
  66. /package/dist/utils/{helper.d.ts → parseFieldValue.d.ts} +0 -0
@@ -1,55 +1,56 @@
1
1
  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[]
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[]
11
11
  }
12
12
 
13
13
  export type AvailableAgents = {
14
- key: string
15
- value: string
16
- formId: string
14
+ key: string
15
+ value: string
16
+ formId: string
17
17
  }
18
18
 
19
19
  export type AgentData = {
20
- id: string
21
- jobId: string
22
- formId: string
20
+ id: string
21
+ jobId: string
22
+ formId: string
23
23
  }
24
24
 
25
25
  export type FieldData = {
26
- name: string
27
- type: string
28
- text: string
29
- applied?: boolean
26
+ id: string
27
+ name: string
28
+ type: string
29
+ text: string
30
+ applied?: boolean
30
31
  }
31
32
 
32
33
  export type AppliedFieldData = {
33
- name: string
34
- text: unknown
34
+ id: string
35
+ text: unknown
35
36
  }
36
37
 
37
38
  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[]
39
+ id: number
40
+ message: string
41
+ date: Date
42
+ sender: ChatBotMessageSender
43
+ sessionID: string
44
+ imagePreviewUrl?: string
45
+ isWelcomeMessage?: boolean
46
+ fields?: FieldData[]
46
47
  }
47
48
 
48
49
  export type ChatBotMessageContent = {
49
- content: string
50
- type: string
51
- sessionID: string
52
- imageUrl?: string
50
+ content: string
51
+ type: string
52
+ sessionID: string
53
+ imageUrl?: string
53
54
  }
54
55
 
55
56
  export type ChatBotMessageSender = 'bot' | 'user'
@@ -1,195 +1,186 @@
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="imagePreviewUrl"
12
+ class="q-chatbot__image-preview">
13
+ <img
14
+ :src="imagePreviewUrl"
15
+ tabindex="0"
16
+ :alt="texts.imagePreview" />
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
+ ref="imageInput"
50
+ type="file"
51
+ :accept="acceptedImageTypes"
52
+ class="hidden-input"
53
+ @change="handleImageUpload" />
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>
66
66
  </template>
67
67
 
68
68
  <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
- }
69
+ // Components
70
+ import { QLabel, QButton, QTextArea, QIcon } 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 props = defineProps<ChatBotInputProps>()
82
+
83
+ const emit = defineEmits<{
84
+ (e: 'send-message', prompt: string, image?: ChatBotImage): void
85
+ }>()
86
+
87
+ const texts = useTexts()
88
+
89
+ const imageInput = ref<HTMLInputElement | null>(null)
90
+ const imagePreviewUrl = ref<string>('')
91
+ const acceptedImageTypes = computed(() => '.png, .jpeg, .jpg, .svg, .webp')
92
+ const hasSelectedImage = ref<boolean>(false)
93
+
94
+ const isDragging = ref(false)
95
+ const userPrompt = ref(props.userPrompt ?? '')
96
+
97
+ const isSendButtonDisabled = computed(() => {
98
+ return props.disabled || props.loading || userPrompt.value.trim().length === 0
99
+ })
100
+
101
+ function onDragOver(event: DragEvent) {
102
+ event.preventDefault()
103
+ if (!props.disabled) {
104
+ isDragging.value = true
105
+ }
106
+ }
107
+
108
+ const chatBotFooterClasses = computed(() => {
109
+ return {
110
+ 'q-chatbot__footer-disabled': props.disabled,
111
+ 'drag-over': isDragging.value && !hasSelectedImage.value
112
+ }
113
+ })
114
+
115
+ function onDragLeave(event: DragEvent) {
116
+ event.preventDefault()
117
+ isDragging.value = false
118
+ }
119
+
120
+ function onDrop(event: DragEvent) {
121
+ event.preventDefault()
122
+ isDragging.value = false
123
+
124
+ if (props.disabled || hasSelectedImage.value) return
125
+
126
+ const files = event.dataTransfer?.files
127
+ if (!files) return
128
+
129
+ const file = files[0]
130
+ if (file.type.startsWith('image/')) {
131
+ if (!imageInput.value) return
132
+
133
+ // Create a new FileList-like object
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
140
+ }
141
+ }
142
+
143
+ function sendMessage() {
144
+ if (userPrompt.value.trim() === '' || props.loading || props.disabled) return
145
+
146
+ // Check if an image is selected
147
+ if (imageInput.value && imageInput.value.files && imageInput.value.files.length > 0) {
148
+ const file = imageInput.value.files[0]
149
+
150
+ const fileData: ChatBotImage = {
151
+ file: file,
152
+ previewUrl: imagePreviewUrl.value
153
+ }
154
+
155
+ emit('send-message', userPrompt.value, fileData)
156
+ removeImage()
157
+ } else emit('send-message', userPrompt.value)
158
+
159
+ userPrompt.value = ''
160
+ }
161
+
162
+ function removeImage() {
163
+ imagePreviewUrl.value = ''
164
+
165
+ if (imageInput.value) {
166
+ imageInput.value.value = ''
167
+ imageInput.value.files = null
168
+ }
169
+ hasSelectedImage.value = false
170
+ }
171
+
172
+ function triggerImageUpload() {
173
+ imageInput.value?.click()
174
+ }
175
+
176
+ function handleImageUpload(event: Event) {
177
+ // Store the selected image in imageInput
178
+ const target = event.target as HTMLInputElement
179
+ imageInput.value = target
180
+
181
+ if (target.files && target.files[0]) {
182
+ imagePreviewUrl.value = URL.createObjectURL(target.files[0])
183
+ hasSelectedImage.value = true
184
+ }
185
+ }
195
186
  </script>