@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.
- package/README.md +1 -2
- package/dist/components/ChatBot/ChatBot.vue.d.ts +2 -0
- package/dist/components/ChatBot/types.d.ts +2 -1
- package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +2 -2
- package/dist/components/ChatBotInput/__tests__/ChatBotInput.spec.d.ts +1 -0
- package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +3 -1
- package/dist/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.d.ts +1 -0
- package/dist/components/ChatToolBar/__tests__/ChatToolBar.spec.d.ts +1 -0
- package/dist/components/FieldPreview/FieldPreview.vue.d.ts +2 -0
- package/dist/components/FieldPreview/__tests__/FieldPreview.spec.d.ts +1 -0
- package/dist/components/MarkdownRender/__tests__/MarkdownRender.spec.d.ts +1 -0
- package/dist/components/PulseDots/__tests__/PulseDots.spec.d.ts +1 -0
- package/dist/composables/__tests__/useChatMessages.spec.d.ts +1 -0
- package/dist/composables/__tests__/useSSE.spec.d.ts +1 -0
- package/dist/composables/useChatApi.d.ts +1 -1
- package/dist/composables/useChatMessages.d.ts +2 -1
- package/dist/composables/useSSE.d.ts +1 -2
- package/dist/composables/useTexts.d.ts +5 -0
- package/dist/index.js +25 -25
- package/dist/index.mjs +2319 -1812
- package/dist/style.css +1 -1
- package/dist/test/setup.d.ts +1 -0
- package/dist/utils/__tests__/parseFieldValue.spec.d.ts +1 -0
- package/package.json +27 -6
- package/src/assets/styles/styles.scss +212 -220
- package/src/components/ChatBot/ChatBot.vue +346 -368
- package/src/components/ChatBot/types.ts +34 -33
- package/src/components/ChatBotInput/ChatBotInput.vue +181 -190
- package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +292 -0
- package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +25 -0
- package/src/components/ChatBotInput/types.ts +24 -24
- package/src/components/ChatBotMessage/ChatBotMessage.vue +133 -134
- package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +179 -164
- package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +199 -0
- package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessageButtons.spec.ts.snap +25 -0
- package/src/components/ChatBotMessage/types.ts +52 -52
- package/src/components/ChatToolBar/ChatToolBar.vue +69 -64
- package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +138 -0
- package/src/components/ChatToolBar/__tests__/__snapshots__/ChatToolBar.spec.ts.snap +11 -0
- package/src/components/ChatToolBar/types.ts +12 -12
- package/src/components/FieldPreview/FieldPreview.vue +83 -63
- package/src/components/FieldPreview/__tests__/FieldPreview.spec.ts +72 -0
- package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +19 -0
- package/src/components/FieldPreview/field-preview.scss +28 -24
- package/src/components/FieldPreview/types.ts +5 -5
- package/src/components/MarkdownRender/MarkdownRender.vue +16 -15
- package/src/components/MarkdownRender/__tests__/MarkdownRender.spec.ts +68 -0
- package/src/components/MarkdownRender/__tests__/__snapshots__/MarkdownRender.spec.ts.snap +8 -0
- package/src/components/MarkdownRender/markdown-render.scss +19 -20
- package/src/components/MarkdownRender/types.ts +3 -3
- package/src/components/PulseDots/PulseDots.vue +17 -17
- package/src/components/PulseDots/__tests__/PulseDots.spec.ts +35 -0
- package/src/components/PulseDots/__tests__/__snapshots__/PulseDots.spec.ts.snap +7 -0
- package/src/components/PulseDots/__tests__/__snapshots__/pulse-dots.spec.ts.snap +7 -0
- package/src/components/PulseDots/pulse-dots.scss +24 -23
- package/src/composables/__tests__/useChatMessages.spec.ts +64 -0
- package/src/composables/__tests__/useSSE.spec.ts +132 -0
- package/src/composables/useChatApi.ts +132 -134
- package/src/composables/useChatMessages.ts +50 -48
- package/src/composables/useSSE.ts +75 -76
- package/src/composables/useTexts.ts +33 -30
- package/src/test/setup.ts +41 -0
- package/src/utils/__tests__/parseFieldValue.spec.ts +27 -0
- package/src/utils/parseFieldValue.ts +12 -0
- package/src/utils/helper.ts +0 -12
- /package/dist/utils/{helper.d.ts → parseFieldValue.d.ts} +0 -0
|
@@ -1,93 +1,92 @@
|
|
|
1
1
|
import axios, { AxiosRequestConfig } from 'axios'
|
|
2
2
|
|
|
3
|
-
type SSEvents = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export type SSEvents = {
|
|
4
|
+
onMessage?: (message: string) => void
|
|
5
|
+
onError?: (error: Error) => void
|
|
6
|
+
onFieldMetadata?: (metadata: Record<string, unknown>) => void
|
|
7
|
+
onDone?: () => void
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export async function useSSE(config: AxiosRequestConfig, handlers: SSEvents) {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const controller = new AbortController()
|
|
12
|
+
const signal = controller.signal
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const response = await axios({
|
|
15
|
+
...config,
|
|
16
|
+
headers: {
|
|
17
|
+
Accept: 'text/event-stream'
|
|
18
|
+
},
|
|
19
|
+
responseType: 'stream',
|
|
20
|
+
signal,
|
|
21
|
+
adapter: 'fetch'
|
|
22
|
+
})
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const stream = response.data
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
if (!stream || !(stream instanceof ReadableStream)) {
|
|
27
|
+
throw new Error('Invalid stream response')
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const reader = stream.getReader()
|
|
31
|
+
const decoder = new TextDecoder()
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
while (true) {
|
|
34
|
+
const { done, value } = await reader.read()
|
|
35
|
+
if (done) {
|
|
36
|
+
handlers.onDone?.()
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const chunk = decoder.decode(value, { stream: true })
|
|
41
|
+
const events = chunk.split(/\n\n+/)
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
for (const eventBlock of events) {
|
|
44
|
+
const lines = eventBlock.trim().split('\n')
|
|
45
|
+
let name = ''
|
|
46
|
+
let data = ''
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (line.startsWith('event:')) {
|
|
50
|
+
name = line.replace('event:', '').trim()
|
|
51
|
+
} else if (line.startsWith('data:')) {
|
|
52
|
+
data = line.replace('data:', '').trim()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
55
|
|
|
56
|
+
// Backward compatibility for missing event names
|
|
57
|
+
if (!name) name = 'message'
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
if(!name) name = 'message';
|
|
59
|
+
if (!data) continue
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
}
|
|
61
|
+
try {
|
|
62
|
+
const parsedData = JSON.parse(data)
|
|
63
|
+
switch (name) {
|
|
64
|
+
case 'message':
|
|
65
|
+
handlers.onMessage?.(parsedData.value)
|
|
66
|
+
break
|
|
67
|
+
case 'error':
|
|
68
|
+
handlers.onError?.(new Error(parsedData.value))
|
|
69
|
+
break
|
|
70
|
+
case 'field_metadata':
|
|
71
|
+
handlers.onFieldMetadata?.(parsedData)
|
|
72
|
+
break
|
|
73
|
+
case 'done':
|
|
74
|
+
handlers.onDone?.()
|
|
75
|
+
break
|
|
76
|
+
default:
|
|
77
|
+
console.warn(`Unknown event type: ${name}`)
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
handlers.onError?.(error as Error)
|
|
81
|
+
console.error('Error processing event:', error)
|
|
82
|
+
continue
|
|
83
|
+
} finally {
|
|
84
|
+
// Ensure we always clean up the reader
|
|
85
|
+
if (done) {
|
|
86
|
+
reader.releaseLock()
|
|
87
|
+
controller.abort()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
93
92
|
}
|
|
@@ -1,32 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO: Implement localization
|
|
3
|
+
*/
|
|
1
4
|
export function useTexts() {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
5
|
+
return {
|
|
6
|
+
copy: 'Copy',
|
|
7
|
+
apply: 'Apply',
|
|
8
|
+
applyAll: 'Apply all',
|
|
9
|
+
chatbotTitle: 'ChatBot',
|
|
10
|
+
sendMessage: 'Send message',
|
|
11
|
+
clearChat: 'Clear chat',
|
|
12
|
+
inputLabel: 'What can I help with?',
|
|
13
|
+
imageUpload: 'Upload Image',
|
|
14
|
+
imageUploadQButton: 'Upload Image',
|
|
15
|
+
goodResponse: 'Good response',
|
|
16
|
+
badResponse: 'Bad response',
|
|
17
|
+
initialMessage:
|
|
18
|
+
"Howdy! I am GenioBot 👋, Quidgest's personal AI assistant! How can I help you?",
|
|
19
|
+
initialAgentMessage: 'Just a temporary message while we are working on the agent mode',
|
|
20
|
+
loginError: 'Uh oh, I could not authenticate with the Quidgest API endpoint 😓',
|
|
21
|
+
botIsSick:
|
|
22
|
+
'*cough cough* GenioBot is not feeling alright 🥴️🤒, looks like something failed!',
|
|
23
|
+
commentDialogTitle: 'Would you like to add a comment?',
|
|
24
|
+
commentPlaceholder: 'Type your comment here (optional)...',
|
|
25
|
+
copyResponse: 'Copy response',
|
|
26
|
+
submitButton: 'Submit',
|
|
27
|
+
cancelButton: 'Cancel',
|
|
28
|
+
senderImage: 'Sender Image',
|
|
29
|
+
imagePreview: 'Image preview',
|
|
30
|
+
regenerateResponsePrompt: 'Regenerate a new response for field {0}',
|
|
31
|
+
regenerateResponse: 'Regenerate response',
|
|
32
|
+
generatingResponse: 'Generating',
|
|
33
|
+
suggestionsForField: 'Suggestions for field'
|
|
34
|
+
}
|
|
32
35
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
2
|
+
import { config } from '@vue/test-utils'
|
|
3
|
+
|
|
4
|
+
import { createFramework } from '@quidgest/ui/framework'
|
|
5
|
+
import { QSelect } from '@quidgest/ui/components'
|
|
6
|
+
|
|
7
|
+
vi.mock('@quidgest/ui/components', async (importOriginal) => {
|
|
8
|
+
const original = await importOriginal<typeof import('@quidgest/ui/components')>()
|
|
9
|
+
return {
|
|
10
|
+
...original,
|
|
11
|
+
// QIcon needs to be mocked to prevent it from making network requests for icons during tests
|
|
12
|
+
QIcon: {
|
|
13
|
+
name: 'QIcon',
|
|
14
|
+
props: ['icon'],
|
|
15
|
+
template: '<span :data-test="icon"><slot /></span>'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Prevent any real network fetches (icons/assets) during tests
|
|
21
|
+
// safe noop that resolves to a minimal response-like object
|
|
22
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
23
|
+
ok: true,
|
|
24
|
+
arrayBuffer: async () => new ArrayBuffer(0),
|
|
25
|
+
blob: async () => new Blob(),
|
|
26
|
+
text: async () => ''
|
|
27
|
+
} as unknown)
|
|
28
|
+
|
|
29
|
+
const framework = createFramework()
|
|
30
|
+
|
|
31
|
+
config.global.plugins = [
|
|
32
|
+
{
|
|
33
|
+
install(app) {
|
|
34
|
+
app.use(framework)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
config.global.components = {
|
|
40
|
+
QSelect
|
|
41
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { parseFieldValue } from '../parseFieldValue'
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect } from 'vitest'
|
|
4
|
+
|
|
5
|
+
describe('parseFieldValue', () => {
|
|
6
|
+
it('parses date strings correctly', () => {
|
|
7
|
+
const date = '2025-09-04'
|
|
8
|
+
const result = parseFieldValue('date', date)
|
|
9
|
+
expect(result).toBe(new Date(date).toLocaleDateString())
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('parses number strings correctly', () => {
|
|
13
|
+
const result = parseFieldValue('number', '123.45')
|
|
14
|
+
expect(result).toBe(123.45)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('parses boolean strings correctly', () => {
|
|
18
|
+
expect(parseFieldValue('boolean', 'true')).toBe(true)
|
|
19
|
+
expect(parseFieldValue('boolean', 'false')).toBe(false)
|
|
20
|
+
expect(parseFieldValue('boolean', 'TRUE')).toBe(true)
|
|
21
|
+
expect(parseFieldValue('boolean', 'FALSE')).toBe(false)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('returns text as string for unknown types', () => {
|
|
25
|
+
expect(parseFieldValue('unknown', '123')).toBe('123')
|
|
26
|
+
})
|
|
27
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function parseFieldValue(type: string, text: string) {
|
|
2
|
+
switch (type) {
|
|
3
|
+
case 'date':
|
|
4
|
+
return new Date(text).toLocaleDateString()
|
|
5
|
+
case 'number':
|
|
6
|
+
return parseFloat(text)
|
|
7
|
+
case 'boolean':
|
|
8
|
+
return text.toLowerCase() === 'true'
|
|
9
|
+
default:
|
|
10
|
+
return text.toString()
|
|
11
|
+
}
|
|
12
|
+
}
|
package/src/utils/helper.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function parseFieldValue(type: string, text: string) {
|
|
2
|
-
switch (type) {
|
|
3
|
-
case 'date':
|
|
4
|
-
return new Date(text).toLocaleDateString()
|
|
5
|
-
case 'number':
|
|
6
|
-
return parseFloat(text)
|
|
7
|
-
case 'boolean':
|
|
8
|
-
return text.toLowerCase() === 'true'
|
|
9
|
-
default:
|
|
10
|
-
return text.toString()
|
|
11
|
-
}
|
|
12
|
-
}
|
|
File without changes
|