@quidgest/chatbot 0.0.1
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.vue.d.ts +57 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4246 -0
- package/dist/index.mjs +4244 -0
- package/dist/style.css +1046 -0
- package/dist/types/message.type.d.ts +6 -0
- package/dist/types/texts.type.d.ts +3 -0
- package/package.json +50 -0
- package/src/assets/chatbot.png +0 -0
- package/src/assets/styles/styles.scss +116 -0
- package/src/components/ChatBot.vue +365 -0
- package/src/index.ts +4 -0
- package/src/shims-vue.d.ts +1 -0
- package/src/types/message.type.ts +6 -0
- package/src/types/texts.type.ts +3 -0
- package/src/vite-env.d.ts +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quidgest/chatbot",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.mjs",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./*": "./*",
|
|
19
|
+
"./style.css": "./dist/style.css"
|
|
20
|
+
},
|
|
21
|
+
"typings": "types/index.d.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "yarn build-lib",
|
|
28
|
+
"build-lib": "rimraf ./dist && vue-tsc --noEmit && vite build",
|
|
29
|
+
"build-types": "vue-tsc --emitDeclarationOnly --declaration -p tsconfig.json",
|
|
30
|
+
"lint": "eslint 'src/**/*.{ts,vue}'"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"axios": "^1.6.7",
|
|
34
|
+
"vue": "^3.3.11",
|
|
35
|
+
"cross-env": "5.0.5"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.11.13",
|
|
39
|
+
"@vitejs/plugin-vue": "^4.5.2",
|
|
40
|
+
"rimraf": "^5.0.5",
|
|
41
|
+
"sass": "^1.70.0",
|
|
42
|
+
"typescript": "^5.2.2",
|
|
43
|
+
"vite": "^5.0.8",
|
|
44
|
+
"vite-plugin-dts": "^3.7.3",
|
|
45
|
+
"vue-tsc": "^1.8.25"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@quidgest/ui": "0.9.7"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
@use '@quidgest/ui';
|
|
2
|
+
|
|
3
|
+
.q-chatbot {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
padding: 1rem;
|
|
7
|
+
|
|
8
|
+
& .q-input-group .i-text__field {
|
|
9
|
+
border-radius: 0;
|
|
10
|
+
flex: 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&__input {
|
|
14
|
+
flex: 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&__content {
|
|
18
|
+
background-color: white;
|
|
19
|
+
border: 1px solid #eaebec;
|
|
20
|
+
height: 100%;
|
|
21
|
+
width: 100%;
|
|
22
|
+
padding: 2rem 1rem;
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
overflow: auto;
|
|
26
|
+
gap: 1.5rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&__message-wrapper {
|
|
30
|
+
display: flex;
|
|
31
|
+
max-width: 100%;
|
|
32
|
+
gap: 0.2rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__message-wrapper_right {
|
|
36
|
+
justify-content: flex-end;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&__profile {
|
|
40
|
+
border-radius: 50%;
|
|
41
|
+
height: 2rem;
|
|
42
|
+
width: 2rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&__message {
|
|
46
|
+
position: relative;
|
|
47
|
+
margin: 0rem 0.5rem;
|
|
48
|
+
max-width: 75%;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
padding: 0.3rem 0.5rem;
|
|
52
|
+
background-color: #eaebec;
|
|
53
|
+
white-space: normal;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&__message-wrapper_right .q-chatbot__message {
|
|
57
|
+
background-color: rgba(#018bd2, 20%);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&__sender {
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
position: absolute;
|
|
63
|
+
color: #7c858d;
|
|
64
|
+
font-size: 0.7rem;
|
|
65
|
+
left: 0;
|
|
66
|
+
top: -1rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&__timestamp {
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
position: absolute;
|
|
72
|
+
color: #7c858d;
|
|
73
|
+
font-size: 0.7rem;
|
|
74
|
+
right: 0;
|
|
75
|
+
top: -1rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&__message-loading {
|
|
79
|
+
display: flex;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 0.3rem;
|
|
83
|
+
|
|
84
|
+
div {
|
|
85
|
+
background-color: rgba(#018bd2, 20%);
|
|
86
|
+
border-radius: 50%;
|
|
87
|
+
width: 0.5rem;
|
|
88
|
+
height: 0.5rem;
|
|
89
|
+
animation: dotPulse 1.5s infinite;
|
|
90
|
+
|
|
91
|
+
&:nth-child(1) {
|
|
92
|
+
animation-delay: 0s;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&:nth-child(2) {
|
|
96
|
+
animation-delay: 0.5s;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&:nth-child(3) {
|
|
100
|
+
animation-delay: 1s;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@keyframes dotPulse {
|
|
107
|
+
0% {
|
|
108
|
+
opacity: 0.5;
|
|
109
|
+
}
|
|
110
|
+
50% {
|
|
111
|
+
opacity: 1;
|
|
112
|
+
}
|
|
113
|
+
100% {
|
|
114
|
+
opacity: 0.5;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, nextTick, computed, ref, watch, defineOptions } from 'vue'
|
|
3
|
+
import type { Ref } from 'vue'
|
|
4
|
+
import Axios from 'axios'
|
|
5
|
+
import type { AxiosResponse } from 'axios'
|
|
6
|
+
import {
|
|
7
|
+
QButton,
|
|
8
|
+
QTextField,
|
|
9
|
+
QInputGroup,
|
|
10
|
+
QIcon
|
|
11
|
+
} from '@quidgest/ui'
|
|
12
|
+
|
|
13
|
+
import type { ResourceStrings } from '@/types/texts.type'
|
|
14
|
+
import type { ChatBotMessage } from '@/types/message.type'
|
|
15
|
+
import ChatBotIcon from '@/assets/chatbot.png'
|
|
16
|
+
|
|
17
|
+
let messages: Ref<ChatBotMessage[]> = ref([])
|
|
18
|
+
let msgHistoryStack: string[] = []
|
|
19
|
+
let nextMessageId: number = 1
|
|
20
|
+
let userPrompt: Ref<string> = ref('');
|
|
21
|
+
let isChatDisabled: boolean = false
|
|
22
|
+
let isLoading: boolean = false
|
|
23
|
+
|
|
24
|
+
// refs
|
|
25
|
+
const messagesContainer = ref<HTMLElement | null>(null)
|
|
26
|
+
const promptInput = ref<HTMLInputElement | null>(null)
|
|
27
|
+
|
|
28
|
+
export type ChatBotProps = {
|
|
29
|
+
/**
|
|
30
|
+
* API Enpoint URL
|
|
31
|
+
*/
|
|
32
|
+
apiEndpoint?: string,
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Static resource texts used by ChatBot
|
|
36
|
+
*/
|
|
37
|
+
texts?: ResourceStrings
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Genio username
|
|
41
|
+
*/
|
|
42
|
+
username: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const props = withDefaults(defineProps<ChatBotProps>(), {
|
|
46
|
+
apiEndpoint: 'http://localhost:3000',
|
|
47
|
+
texts: () => ({
|
|
48
|
+
chatbotTitle: 'ChatBot',
|
|
49
|
+
qButtonTitle: 'Send message',
|
|
50
|
+
placeholderMessage: 'Type your message here...',
|
|
51
|
+
initialMessage:
|
|
52
|
+
"Howdy! I am GenioBot 👋, Quidgest's personal AI assistant! How can I help you?",
|
|
53
|
+
loginError:
|
|
54
|
+
'Uh oh, I could not authenticate with the Quidgest API endpoint 😓',
|
|
55
|
+
botIsSick:
|
|
56
|
+
'*cough cough* GenioBot is not feeling alright 🥴️🤒, looks like something failed!'
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
onMounted(() => {
|
|
61
|
+
initChat()
|
|
62
|
+
nextTick(scrollChatToBottom)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const getParsedEndpoint = computed(() => {
|
|
66
|
+
try {
|
|
67
|
+
let url = new URL(props.apiEndpoint).href
|
|
68
|
+
|
|
69
|
+
if (url.charAt(url.length - 1) == '/') {
|
|
70
|
+
url = url.slice(0, -1)
|
|
71
|
+
}
|
|
72
|
+
return url
|
|
73
|
+
} catch (ex) {
|
|
74
|
+
addChatMessage(props.texts.botIsSick)
|
|
75
|
+
|
|
76
|
+
isChatDisabled = true
|
|
77
|
+
throw Error('Could not parse Endpoint URL:\n' + ex)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const sortedMessages = computed(() => {
|
|
82
|
+
return messages.value.toSorted((a: ChatBotMessage, b: ChatBotMessage) => {
|
|
83
|
+
const diff = new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
84
|
+
return diff !== 0 ? diff : a.sender === 'user' ? -1 : 1
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const userMessages = computed(() => {
|
|
89
|
+
return messages.value.filter((m: ChatBotMessage) => m.sender === 'user')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
function initChat() {
|
|
93
|
+
Axios.post(getParsedEndpoint.value + '/auth/login', {
|
|
94
|
+
username: props.username,
|
|
95
|
+
password: 'test'
|
|
96
|
+
})
|
|
97
|
+
.then((response: AxiosResponse) => {
|
|
98
|
+
if (response.status != 200 || !response.data.success) {
|
|
99
|
+
isChatDisabled = true
|
|
100
|
+
addChatMessage(props.texts.loginError)
|
|
101
|
+
return console.log(
|
|
102
|
+
`Unsuccessful login, endpoint gave status ${response.status}`
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
sendInitialMessage()
|
|
107
|
+
})
|
|
108
|
+
.catch((error: Error) => {
|
|
109
|
+
if (error) {
|
|
110
|
+
isChatDisabled = true
|
|
111
|
+
addChatMessage(props.texts.loginError)
|
|
112
|
+
console.log(
|
|
113
|
+
'The following error ocurred while trying to login: \n' +
|
|
114
|
+
error
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function addChatMessage(message: string, sender: 'bot' | 'user' = 'bot') {
|
|
121
|
+
messages.value.push({
|
|
122
|
+
id: nextMessageId++,
|
|
123
|
+
text: message,
|
|
124
|
+
timestamp: new Date(),
|
|
125
|
+
sender: sender
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getLastMessage() {
|
|
130
|
+
return messages.value.find(
|
|
131
|
+
(m: ChatBotMessage) => m.id === nextMessageId - 1
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function sendInitialMessage() {
|
|
136
|
+
addChatMessage(props.texts.initialMessage)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resetChat() {
|
|
140
|
+
messages.value = []
|
|
141
|
+
msgHistoryStack = []
|
|
142
|
+
userPrompt.value = ''
|
|
143
|
+
isLoading = false
|
|
144
|
+
isChatDisabled = false
|
|
145
|
+
|
|
146
|
+
sendInitialMessage()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function scrollChatToBottom() {
|
|
150
|
+
if(messagesContainer.value == null) return;
|
|
151
|
+
|
|
152
|
+
const element = messagesContainer.value
|
|
153
|
+
setTimeout(
|
|
154
|
+
() =>
|
|
155
|
+
(element.scrollIntoView(false)),
|
|
156
|
+
100
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function handleKey(event: KeyboardEvent) {
|
|
161
|
+
if(promptInput.value == null) return;
|
|
162
|
+
|
|
163
|
+
if (event.key == 'ArrowUp') {
|
|
164
|
+
//No user messages, no need to continue
|
|
165
|
+
if (userMessages.value.length == 0) return
|
|
166
|
+
|
|
167
|
+
//Get next message to read
|
|
168
|
+
let lastMsgObj =
|
|
169
|
+
userMessages.value[
|
|
170
|
+
userMessages.value.length -
|
|
171
|
+
1 -
|
|
172
|
+
msgHistoryStack.length
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
//No more messages to go through
|
|
176
|
+
if (!lastMsgObj) return
|
|
177
|
+
|
|
178
|
+
//Save current prompt (even if modified) & update input
|
|
179
|
+
msgHistoryStack.push(userPrompt.value)
|
|
180
|
+
userPrompt.value = lastMsgObj.text
|
|
181
|
+
|
|
182
|
+
//Set the cursor to the end of text
|
|
183
|
+
nextTick(() =>
|
|
184
|
+
setCursorPosition(
|
|
185
|
+
promptInput.value as HTMLInputElement,
|
|
186
|
+
lastMsgObj.text.length
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
} else if (event.key == 'ArrowDown') {
|
|
190
|
+
let previousHistoryText = msgHistoryStack.pop()
|
|
191
|
+
|
|
192
|
+
if (!previousHistoryText) {
|
|
193
|
+
//No more prompts in the stack
|
|
194
|
+
userPrompt.value = ''
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
userPrompt.value = previousHistoryText
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function sendMessage() {
|
|
203
|
+
if (
|
|
204
|
+
userPrompt.value.trim().length == 0 ||
|
|
205
|
+
isLoading ||
|
|
206
|
+
isChatDisabled
|
|
207
|
+
)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
addChatMessage(userPrompt.value, 'user')
|
|
211
|
+
|
|
212
|
+
scrollChatToBottom()
|
|
213
|
+
setChatPrompt(userPrompt.value)
|
|
214
|
+
|
|
215
|
+
userPrompt.value = '' //Clear user input
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function setChatPrompt(prompt: string) {
|
|
219
|
+
addChatMessage('') //Create empty bot message
|
|
220
|
+
let msg = getLastMessage()
|
|
221
|
+
|
|
222
|
+
//Send message request
|
|
223
|
+
let params = {
|
|
224
|
+
message: prompt,
|
|
225
|
+
project: 'GenIO',
|
|
226
|
+
user: props.username
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
isLoading = true
|
|
230
|
+
Axios({
|
|
231
|
+
url: getParsedEndpoint.value + '/prompt/message',
|
|
232
|
+
method: 'POST',
|
|
233
|
+
data: params,
|
|
234
|
+
onDownloadProgress: (progressEvent) => {
|
|
235
|
+
const chunk =
|
|
236
|
+
progressEvent.event?.currentTarget.response
|
|
237
|
+
const status = progressEvent.event?.currentTarget.status
|
|
238
|
+
|
|
239
|
+
if (status != 200) return
|
|
240
|
+
|
|
241
|
+
if (msg) msg.text = chunk
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
.then(({ data }) => {
|
|
245
|
+
if (msg) msg.text = data
|
|
246
|
+
})
|
|
247
|
+
.catch((error) => {
|
|
248
|
+
addChatMessage(props.texts.botIsSick)
|
|
249
|
+
|
|
250
|
+
isChatDisabled = true
|
|
251
|
+
console.log(error)
|
|
252
|
+
})
|
|
253
|
+
.finally(() => {
|
|
254
|
+
isLoading = false
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function getSenderName(sender: 'bot' | 'user') {
|
|
259
|
+
return sender === 'bot' ? 'GenioBot' : 'You'
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function getConvertedTime(date: Date) {
|
|
263
|
+
return date.toLocaleString()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function setCursorPosition(elem: HTMLInputElement, pos: number) {
|
|
267
|
+
elem.focus()
|
|
268
|
+
elem.setSelectionRange(pos, pos)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
watch(() => props.apiEndpoint, () => {
|
|
272
|
+
resetChat()
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
defineOptions({ name: 'ChatBot' })
|
|
276
|
+
</script>
|
|
277
|
+
|
|
278
|
+
<template>
|
|
279
|
+
<div class="q-chatbot">
|
|
280
|
+
<div class="c-sidebar__subtitle">
|
|
281
|
+
<span>{{ props.texts.chatbotTitle }}</span>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<div
|
|
285
|
+
class="q-chatbot__content"
|
|
286
|
+
ref="messagesContainer">
|
|
287
|
+
<div
|
|
288
|
+
v-for="message in sortedMessages"
|
|
289
|
+
:key="message.id"
|
|
290
|
+
:class="[
|
|
291
|
+
'q-chatbot__message-wrapper',
|
|
292
|
+
{
|
|
293
|
+
'q-chatbot__message-wrapper_right':
|
|
294
|
+
message.sender == 'user'
|
|
295
|
+
}
|
|
296
|
+
]">
|
|
297
|
+
<img
|
|
298
|
+
v-if="message.sender == 'bot'"
|
|
299
|
+
:src="ChatBotIcon"
|
|
300
|
+
alt=""
|
|
301
|
+
class="q-chatbot__profile" />
|
|
302
|
+
|
|
303
|
+
<div class="q-chatbot__message">
|
|
304
|
+
<div
|
|
305
|
+
v-if="isLoading && !message.text"
|
|
306
|
+
class="q-chatbot__message-loading">
|
|
307
|
+
<div></div>
|
|
308
|
+
<div></div>
|
|
309
|
+
<div></div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<template v-else>
|
|
313
|
+
<div
|
|
314
|
+
class="q-chatbot__sender"
|
|
315
|
+
v-if="message.text && message.sender == 'bot'">
|
|
316
|
+
{{
|
|
317
|
+
getSenderName(message.sender) +
|
|
318
|
+
' ' +
|
|
319
|
+
getConvertedTime(message.timestamp)
|
|
320
|
+
}}
|
|
321
|
+
</div>
|
|
322
|
+
<div
|
|
323
|
+
class="q-chatbot__timestamp"
|
|
324
|
+
v-if="message.text && message.sender == 'user'">
|
|
325
|
+
{{ getConvertedTime(message.timestamp) }}
|
|
326
|
+
</div>
|
|
327
|
+
<div
|
|
328
|
+
class="q-chatbot__text"
|
|
329
|
+
v-if="message.sender == 'bot'"
|
|
330
|
+
v-html="message.text"></div>
|
|
331
|
+
<div
|
|
332
|
+
class="q-chatbot__text"
|
|
333
|
+
v-else>
|
|
334
|
+
{{ message.text }}
|
|
335
|
+
</div>
|
|
336
|
+
</template>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<q-input-group
|
|
342
|
+
size="block"
|
|
343
|
+
:disabled="isChatDisabled">
|
|
344
|
+
<q-text-field
|
|
345
|
+
ref="promptInput"
|
|
346
|
+
v-model="userPrompt"
|
|
347
|
+
class="q-chatbot__input"
|
|
348
|
+
:placeholder="props.texts.placeholderMessage"
|
|
349
|
+
:disabled="isChatDisabled"
|
|
350
|
+
@keyup.enter="sendMessage"
|
|
351
|
+
@keydown="handleKey" />
|
|
352
|
+
|
|
353
|
+
<template #append>
|
|
354
|
+
<q-button
|
|
355
|
+
:title="props.texts.qButtonTitle"
|
|
356
|
+
b-style="primary"
|
|
357
|
+
class="q-chatbot__send"
|
|
358
|
+
:disabled="isChatDisabled"
|
|
359
|
+
@click="sendMessage">
|
|
360
|
+
<q-icon icon="send" />
|
|
361
|
+
</q-button>
|
|
362
|
+
</template>
|
|
363
|
+
</q-input-group>
|
|
364
|
+
</div>
|
|
365
|
+
</template>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module '*.vue'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|