rushchatbot 1.6.9 → 1.7.2
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 -1
- package/package/INTEGRATION.md +1 -1
- package/package/README.md +1 -1
- package/package/package.json +1 -1
- package/package/src/client/ChatWidget.tsx +13 -0
- package/package/src/core/RushChatbot.ts +26 -1
- package/package/src/server/index.ts +19 -6
- package/package/src/shared/types.ts +2 -1
- package/package/src/vanilla/index.ts +122 -30
- package/package/src/vue/ChatWidget.vue +5 -1
- package/package/src/vue2/ChatWidget.vue +5 -1
- package/package.json +1 -1
- package/server/data/analytics.json +66 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.2
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/INTEGRATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.0
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.2
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/package.json
CHANGED
|
@@ -44,6 +44,9 @@ function ThinkingDots({ color }: { color: string }) {
|
|
|
44
44
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
45
45
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
46
46
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
47
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
48
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
49
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
47
50
|
`}</style>
|
|
48
51
|
</span>
|
|
49
52
|
)
|
|
@@ -139,6 +142,16 @@ export function ChatWidget({ apiBase, width = 380, height = 580 }: ChatWidgetPro
|
|
|
139
142
|
</div>
|
|
140
143
|
)}
|
|
141
144
|
|
|
145
|
+
{!state.messages.some(m => m.isEndChat) && (
|
|
146
|
+
<button onClick={() => chat.endChat()} style={{
|
|
147
|
+
textAlign: 'left', padding: '8px 12px', borderRadius: 8,
|
|
148
|
+
border: '1px solid #EF4444', background: 'transparent',
|
|
149
|
+
color: '#EF4444', cursor: 'pointer', fontSize: 14, marginTop: 4, width: '100%',
|
|
150
|
+
}}>
|
|
151
|
+
👋 I'm done, thanks!
|
|
152
|
+
</button>
|
|
153
|
+
)}
|
|
154
|
+
|
|
142
155
|
<div ref={bottomRef} />
|
|
143
156
|
</div>
|
|
144
157
|
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import { marked } from 'marked'
|
|
2
2
|
import type { Message, Question, WidgetSettings } from '../shared/types'
|
|
3
3
|
|
|
4
|
-
marked.
|
|
4
|
+
marked.use({
|
|
5
|
+
breaks: true,
|
|
6
|
+
renderer: {
|
|
7
|
+
code({ text, lang }: { text: string; lang?: string }): string {
|
|
8
|
+
const escaped = text
|
|
9
|
+
.replace(/&/g, '&')
|
|
10
|
+
.replace(/</g, '<')
|
|
11
|
+
.replace(/>/g, '>')
|
|
12
|
+
const langAttr = lang ? ` data-lang="${lang}"` : ''
|
|
13
|
+
return `<pre${langAttr}><code>${escaped}</code></pre>`
|
|
14
|
+
},
|
|
15
|
+
codespan({ text }: { text: string }): string {
|
|
16
|
+
const escaped = text
|
|
17
|
+
.replace(/&/g, '&')
|
|
18
|
+
.replace(/</g, '<')
|
|
19
|
+
.replace(/>/g, '>')
|
|
20
|
+
return `<code>${escaped}</code>`
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
})
|
|
5
24
|
|
|
6
25
|
export interface RushChatbotConfig {
|
|
7
26
|
apiBase?: string
|
|
@@ -170,6 +189,12 @@ export class RushChatbot {
|
|
|
170
189
|
|
|
171
190
|
this.setState({ loading: false })
|
|
172
191
|
|
|
192
|
+
if (res.source === 'n8n_error' || res.source === 'error') {
|
|
193
|
+
this.addMessage({ role: 'bot', text: res.answer ?? "I'm having trouble right now. Please try again in a moment. ⏳", source: res.source, isError: true })
|
|
194
|
+
this.setState({ showMain: true })
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
173
198
|
if (res.paragraphs?.length > 0) {
|
|
174
199
|
for (const p of res.paragraphs) {
|
|
175
200
|
this.addMessage({ role: 'bot', text: p, source: res.source })
|
|
@@ -224,12 +224,25 @@ export function createServer(options: RushChatbotServerOptions) {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
saveAnalytics({ sessionId: sid, questionId: 'n8n', timestamp: new Date().toISOString(), type: 'n8n' })
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
try {
|
|
228
|
+
const { data } = await axios.post(
|
|
229
|
+
webhookUrl,
|
|
230
|
+
{ query: query.trim(), sessionId: sid },
|
|
231
|
+
{ timeout: timeoutMs, headers: { 'Content-Type': 'application/json', 'x-session-id': sid } }
|
|
232
|
+
)
|
|
233
|
+
return res.json({ ...parseN8nResponse(data), source: 'n8n' })
|
|
234
|
+
} catch (n8nErr: unknown) {
|
|
235
|
+
const n8nMsg = n8nErr instanceof Error ? n8nErr.message : String(n8nErr)
|
|
236
|
+
const isTimeout = n8nMsg.includes('timeout') || n8nMsg.includes('ECONNREFUSED') || n8nMsg.includes('ENOTFOUND')
|
|
237
|
+
console.error('[RushChatbot n8n error]', sanitizeLog(n8nMsg))
|
|
238
|
+
saveAnalytics({ sessionId: sid, questionId: 'n8n_error', timestamp: new Date().toISOString(), type: 'error' })
|
|
239
|
+
return res.status(502).json({
|
|
240
|
+
answer: isTimeout
|
|
241
|
+
? "I'm having trouble reaching my knowledge base right now. Please try again in a moment. ⏳"
|
|
242
|
+
: cfg.chat.fallbackMessage,
|
|
243
|
+
source: 'n8n_error',
|
|
244
|
+
})
|
|
245
|
+
}
|
|
233
246
|
|
|
234
247
|
} catch (err: unknown) {
|
|
235
248
|
const message = err instanceof Error ? err.message : String(err)
|
|
@@ -38,10 +38,11 @@ export interface Message {
|
|
|
38
38
|
id: string
|
|
39
39
|
role: 'user' | 'bot'
|
|
40
40
|
text: string
|
|
41
|
-
source?: 'config' | 'n8n' | 'error'
|
|
41
|
+
source?: 'config' | 'n8n' | 'n8n_error' | 'error'
|
|
42
42
|
subquestions?: Question[]
|
|
43
43
|
isFollowUp?: boolean
|
|
44
44
|
isEndChat?: boolean
|
|
45
|
+
isError?: boolean
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export interface WidgetSettings {
|
|
@@ -56,6 +56,9 @@ const STYLES = `
|
|
|
56
56
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
57
57
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
58
58
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
59
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
60
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
61
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
59
62
|
`
|
|
60
63
|
|
|
61
64
|
const OBSERVED = [
|
|
@@ -71,11 +74,19 @@ class RushChatbotElement extends HTMLElement {
|
|
|
71
74
|
private state: RushChatbotState | null = null
|
|
72
75
|
private showScrollBtn = false
|
|
73
76
|
private resizeObserver: ResizeObserver | null = null
|
|
77
|
+
private isOpen = false
|
|
78
|
+
private btnX = -1
|
|
79
|
+
private btnY = -1
|
|
80
|
+
private dragging = false
|
|
81
|
+
private dragOffX = 0
|
|
82
|
+
private dragOffY = 0
|
|
83
|
+
private dragMoved = false
|
|
74
84
|
|
|
75
85
|
static get observedAttributes() { return OBSERVED }
|
|
76
86
|
|
|
77
87
|
connectedCallback() {
|
|
78
88
|
this.shadow = this.attachShadow({ mode: 'open' })
|
|
89
|
+
this.isOpen = this.readSettings().autoOpen ?? false
|
|
79
90
|
this.chat = new RushChatbot({ apiBase: this.getAttribute('api-base') ?? undefined, settings: this.readSettings() })
|
|
80
91
|
this.chat.subscribe(state => { this.state = state; this.render() })
|
|
81
92
|
this.chat.init()
|
|
@@ -125,23 +136,35 @@ class RushChatbotElement extends HTMLElement {
|
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
private
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
private initBtnPos() {
|
|
140
|
+
const pos = this.settings.position ?? 'bottom-right'
|
|
141
|
+
const margin = 24
|
|
142
|
+
const btnSize = 56
|
|
143
|
+
if (this.btnX === -1) {
|
|
144
|
+
this.btnX = pos === 'bottom-left'
|
|
145
|
+
? margin
|
|
146
|
+
: window.innerWidth - btnSize - margin
|
|
147
|
+
this.btnY = window.innerHeight - btnSize - margin
|
|
148
|
+
}
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
private render() {
|
|
138
152
|
if (!this.state || !this.state.config) return
|
|
139
153
|
const { theme, state, settings } = this
|
|
140
154
|
|
|
155
|
+
this.initBtnPos()
|
|
156
|
+
|
|
141
157
|
const borderRadius = settings.borderRadius ?? 16
|
|
142
158
|
const shadow = settings.showShadow !== false ? '0 8px 32px rgba(0,0,0,0.15)' : 'none'
|
|
143
159
|
const placeholder = settings.placeholder ?? 'Type a message...'
|
|
144
160
|
const sendLabel = settings.sendLabel ?? 'Send'
|
|
161
|
+
const chatW = this.getAttribute('width') ? Number(this.getAttribute('width')) : 380
|
|
162
|
+
const chatH = this.getAttribute('height') ? Number(this.getAttribute('height')) : 580
|
|
163
|
+
const btnSize = 56
|
|
164
|
+
|
|
165
|
+
// Clamp chat box so it stays in viewport
|
|
166
|
+
const boxLeft = Math.min(Math.max(this.btnX + btnSize / 2 - chatW / 2, 8), window.innerWidth - chatW - 8)
|
|
167
|
+
const boxBottom = window.innerHeight - this.btnY + 12
|
|
145
168
|
|
|
146
169
|
const msgsEl = this.shadow.querySelector('.messages') as HTMLElement | null
|
|
147
170
|
const wasAtBottom = msgsEl ? msgsEl.scrollHeight - msgsEl.scrollTop - msgsEl.clientHeight < 60 : true
|
|
@@ -149,32 +172,46 @@ class RushChatbotElement extends HTMLElement {
|
|
|
149
172
|
// Build static shell with no user data in innerHTML
|
|
150
173
|
const shellHtml = `
|
|
151
174
|
<style>${STYLES}</style>
|
|
152
|
-
<div
|
|
153
|
-
<div class="
|
|
154
|
-
<div class="header
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
<div id="chat-box" style="display:${this.isOpen ? 'flex' : 'none'};position:fixed;left:${boxLeft}px;bottom:${boxBottom}px;z-index:2147483646;flex-direction:column;width:${chatW}px;height:${chatH}px">
|
|
176
|
+
<div class="container" id="container">
|
|
177
|
+
<div class="header" id="header">
|
|
178
|
+
<div style="display:flex;align-items:center;justify-content:space-between">
|
|
179
|
+
<div>
|
|
180
|
+
<div class="header-title" id="header-title"></div>
|
|
181
|
+
<div class="header-subtitle" id="header-subtitle"></div>
|
|
182
|
+
</div>
|
|
183
|
+
<button id="close-btn" style="background:none;border:none;color:#fff;cursor:pointer;font-size:20px;line-height:1;padding:0 4px">×</button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
<div class="messages-wrap">
|
|
187
|
+
<div class="messages" id="msgs">
|
|
188
|
+
<div id="msgs-content"></div>
|
|
189
|
+
<div id="bottom"></div>
|
|
190
|
+
</div>
|
|
161
191
|
</div>
|
|
192
|
+
${settings.hideInput ? '' : `
|
|
193
|
+
<form class="input-row" id="form">
|
|
194
|
+
<input id="input" />
|
|
195
|
+
<button type="submit" id="send-btn"></button>
|
|
196
|
+
</form>`}
|
|
162
197
|
</div>
|
|
163
|
-
${settings.hideInput ? '' : `
|
|
164
|
-
<form class="input-row" id="form">
|
|
165
|
-
<input id="input" />
|
|
166
|
-
<button type="submit" id="send-btn"></button>
|
|
167
|
-
</form>`}
|
|
168
198
|
</div>
|
|
199
|
+
<button id="fab" style="position:fixed;left:${this.btnX}px;top:${this.btnY}px;z-index:2147483647;width:${btnSize}px;height:${btnSize}px;border-radius:50%;border:none;cursor:grab;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px rgba(0,0,0,0.25);touch-action:none">
|
|
200
|
+
<svg id="fab-icon-chat" width="26" height="26" viewBox="0 0 24 24" fill="none" style="display:${this.isOpen ? 'none' : 'block'}"><path d="M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill="#fff"/></svg>
|
|
201
|
+
<svg id="fab-icon-close" width="22" height="22" viewBox="0 0 24 24" fill="none" style="display:${this.isOpen ? 'block' : 'none'}"><path d="M18 6L6 18M6 6l12 12" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
|
|
202
|
+
</button>
|
|
169
203
|
`
|
|
170
204
|
this.shadow.innerHTML = shellHtml
|
|
171
205
|
|
|
172
206
|
// Populate dynamic values via DOM (safe from XSS)
|
|
173
207
|
const container = this.shadow.getElementById('container') as HTMLElement
|
|
174
|
-
container.style.cssText = `background:${escapeHtml(theme.backgroundColor)};color:${escapeHtml(theme.textColor)};width
|
|
208
|
+
container.style.cssText = `background:${escapeHtml(theme.backgroundColor)};color:${escapeHtml(theme.textColor)};width:100%;height:100%;font-family:${escapeHtml(theme.fontFamily)};border-radius:${borderRadius}px;box-shadow:${shadow};display:flex;flex-direction:column;overflow:hidden`
|
|
209
|
+
|
|
210
|
+
const fab = this.shadow.getElementById('fab') as HTMLElement
|
|
211
|
+
fab.style.background = escapeHtml(theme.primaryColor)
|
|
175
212
|
|
|
176
213
|
const header = this.shadow.getElementById('header') as HTMLElement
|
|
177
|
-
header.style.
|
|
214
|
+
header.style.cssText = `background:${escapeHtml(theme.primaryColor)};padding:16px 20px;color:#fff`
|
|
178
215
|
;(this.shadow.getElementById('header-title') as HTMLElement).textContent = state.config!.branding.title
|
|
179
216
|
;(this.shadow.getElementById('header-subtitle') as HTMLElement).textContent = state.config!.branding.subtitle
|
|
180
217
|
|
|
@@ -210,16 +247,17 @@ class RushChatbotElement extends HTMLElement {
|
|
|
210
247
|
btn.textContent = q.question
|
|
211
248
|
mainList.appendChild(btn)
|
|
212
249
|
})
|
|
213
|
-
if (!settings.hideEndChat) {
|
|
214
|
-
const endBtn = document.createElement('button')
|
|
215
|
-
endBtn.className = 'end-btn'
|
|
216
|
-
endBtn.dataset.action = 'endchat'
|
|
217
|
-
endBtn.textContent = "\uD83D\uDC4B I'm done, thanks!"
|
|
218
|
-
mainList.appendChild(endBtn)
|
|
219
|
-
}
|
|
220
250
|
msgsContent.appendChild(mainList)
|
|
221
251
|
}
|
|
222
252
|
|
|
253
|
+
if (!settings.hideEndChat && !state.messages.some(m => m.isEndChat)) {
|
|
254
|
+
const endBtn = document.createElement('button')
|
|
255
|
+
endBtn.className = 'end-btn'
|
|
256
|
+
endBtn.dataset.action = 'endchat'
|
|
257
|
+
endBtn.textContent = "\uD83D\uDC4B I'm done, thanks!"
|
|
258
|
+
msgsContent.appendChild(endBtn)
|
|
259
|
+
}
|
|
260
|
+
|
|
223
261
|
if (this.showScrollBtn) {
|
|
224
262
|
const scrollBtn = document.createElement('button')
|
|
225
263
|
scrollBtn.className = 'scroll-btn'
|
|
@@ -242,6 +280,7 @@ class RushChatbotElement extends HTMLElement {
|
|
|
242
280
|
}
|
|
243
281
|
|
|
244
282
|
const newMsgsEl = this.shadow.getElementById('msgs') as HTMLElement
|
|
283
|
+
if (!this.isOpen) return
|
|
245
284
|
if (wasAtBottom) {
|
|
246
285
|
newMsgsEl.scrollTop = newMsgsEl.scrollHeight
|
|
247
286
|
this.showScrollBtn = false
|
|
@@ -262,6 +301,55 @@ class RushChatbotElement extends HTMLElement {
|
|
|
262
301
|
}
|
|
263
302
|
|
|
264
303
|
this.bindEvents()
|
|
304
|
+
this.bindDrag()
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private bindDrag() {
|
|
308
|
+
const fab = this.shadow.getElementById('fab') as HTMLElement
|
|
309
|
+
if (!fab) return
|
|
310
|
+
|
|
311
|
+
fab.addEventListener('pointerdown', (e: PointerEvent) => {
|
|
312
|
+
this.dragging = true
|
|
313
|
+
this.dragMoved = false
|
|
314
|
+
this.dragOffX = e.clientX - this.btnX
|
|
315
|
+
this.dragOffY = e.clientY - this.btnY
|
|
316
|
+
fab.setPointerCapture(e.pointerId)
|
|
317
|
+
fab.style.cursor = 'grabbing'
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
fab.addEventListener('pointermove', (e: PointerEvent) => {
|
|
321
|
+
if (!this.dragging) return
|
|
322
|
+
const newX = e.clientX - this.dragOffX
|
|
323
|
+
const newY = e.clientY - this.dragOffY
|
|
324
|
+
if (Math.abs(newX - this.btnX) > 4 || Math.abs(newY - this.btnY) > 4) this.dragMoved = true
|
|
325
|
+
this.btnX = Math.min(Math.max(newX, 0), window.innerWidth - 56)
|
|
326
|
+
this.btnY = Math.min(Math.max(newY, 0), window.innerHeight - 56)
|
|
327
|
+
fab.style.left = `${this.btnX}px`
|
|
328
|
+
fab.style.top = `${this.btnY}px`
|
|
329
|
+
const chatBox = this.shadow.getElementById('chat-box') as HTMLElement
|
|
330
|
+
if (chatBox && this.isOpen) {
|
|
331
|
+
const chatW = chatBox.offsetWidth || 380
|
|
332
|
+
const chatH = chatBox.offsetHeight || 580
|
|
333
|
+
const boxLeft = Math.min(Math.max(this.btnX + 28 - chatW / 2, 8), window.innerWidth - chatW - 8)
|
|
334
|
+
const boxBottom = window.innerHeight - this.btnY + 12
|
|
335
|
+
chatBox.style.left = `${boxLeft}px`
|
|
336
|
+
chatBox.style.bottom = `${boxBottom}px`
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
fab.addEventListener('pointerup', () => {
|
|
341
|
+
if (!this.dragMoved) {
|
|
342
|
+
this.isOpen = !this.isOpen
|
|
343
|
+
this.render()
|
|
344
|
+
}
|
|
345
|
+
this.dragging = false
|
|
346
|
+
fab.style.cursor = 'grab'
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
this.shadow.getElementById('close-btn')?.addEventListener('click', () => {
|
|
350
|
+
this.isOpen = false
|
|
351
|
+
this.render()
|
|
352
|
+
})
|
|
265
353
|
}
|
|
266
354
|
|
|
267
355
|
private bindEvents() {
|
|
@@ -372,6 +460,10 @@ class RushChatbotElement extends HTMLElement {
|
|
|
372
460
|
mdDiv.className = 'rushchatbot-md'
|
|
373
461
|
mdDiv.innerHTML = parseMarkdown(msg.text)
|
|
374
462
|
bubble.appendChild(mdDiv)
|
|
463
|
+
if (msg.isError) {
|
|
464
|
+
bubble.style.border = '1px solid #EF4444'
|
|
465
|
+
bubble.style.color = '#B91C1C'
|
|
466
|
+
}
|
|
375
467
|
if (msg.isEndChat) {
|
|
376
468
|
const newChatBtn = document.createElement('button')
|
|
377
469
|
newChatBtn.className = 'new-chat-btn'
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
:style="mainBtnStyle">
|
|
43
43
|
{{ q.question }}
|
|
44
44
|
</button>
|
|
45
|
-
<button v-if="!hideEndChat" @click="chat.endChat()" :style="endBtnStyle">👋 I'm done, thanks!</button>
|
|
46
45
|
</div>
|
|
47
46
|
|
|
47
|
+
<button v-if="!hideEndChat && !state.messages.some(m => m.isEndChat)" @click="chat.endChat()" :style="endBtnStyle">👋 I'm done, thanks!</button>
|
|
48
|
+
|
|
48
49
|
<div ref="bottom" />
|
|
49
50
|
</div>
|
|
50
51
|
|
|
@@ -195,4 +196,7 @@ const sendBtnStyle = computed(() => ({
|
|
|
195
196
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
196
197
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
197
198
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
199
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
200
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
201
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
198
202
|
</style>
|
|
@@ -44,9 +44,10 @@
|
|
|
44
44
|
:style="mainBtnStyle">
|
|
45
45
|
{{ q.question }}
|
|
46
46
|
</button>
|
|
47
|
-
<button v-if="!hideEndChat" @click="chat.endChat()" :style="endBtnStyle">👋 I'm done, thanks!</button>
|
|
48
47
|
</div>
|
|
49
48
|
|
|
49
|
+
<button v-if="!hideEndChat && !state.messages.some(function(m) { return m.isEndChat })" @click="chat.endChat()" :style="endBtnStyle">👋 I'm done, thanks!</button>
|
|
50
|
+
|
|
50
51
|
<div ref="bottom" />
|
|
51
52
|
</div>
|
|
52
53
|
|
|
@@ -201,4 +202,7 @@ export default {
|
|
|
201
202
|
0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
202
203
|
40% { transform: translateY(-6px); opacity: 1; }
|
|
203
204
|
}
|
|
205
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
206
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
207
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
204
208
|
</style>
|
package/package.json
CHANGED
|
@@ -797,5 +797,71 @@
|
|
|
797
797
|
"questionId": "q1",
|
|
798
798
|
"timestamp": "2026-09-25T06:14:13.722Z",
|
|
799
799
|
"type": "click"
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
803
|
+
"questionId": "q3",
|
|
804
|
+
"timestamp": "2026-09-25T07:29:56.881Z",
|
|
805
|
+
"type": "click"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
809
|
+
"questionId": "q3h",
|
|
810
|
+
"timestamp": "2026-09-25T07:29:59.276Z",
|
|
811
|
+
"type": "click"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
815
|
+
"questionId": "q6",
|
|
816
|
+
"timestamp": "2026-09-25T07:30:05.858Z",
|
|
817
|
+
"type": "click"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
821
|
+
"questionId": "q8",
|
|
822
|
+
"timestamp": "2026-09-25T07:31:58.988Z",
|
|
823
|
+
"type": "click"
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
827
|
+
"questionId": "q8c",
|
|
828
|
+
"timestamp": "2026-09-25T07:32:01.577Z",
|
|
829
|
+
"type": "click"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
833
|
+
"questionId": "q8",
|
|
834
|
+
"timestamp": "2026-09-25T08:03:16.219Z",
|
|
835
|
+
"type": "click"
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
839
|
+
"questionId": "q8c",
|
|
840
|
+
"timestamp": "2026-09-25T08:03:18.364Z",
|
|
841
|
+
"type": "click"
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
845
|
+
"questionId": "q8b",
|
|
846
|
+
"timestamp": "2026-09-25T08:03:21.319Z",
|
|
847
|
+
"type": "click"
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
851
|
+
"questionId": "q8a",
|
|
852
|
+
"timestamp": "2026-09-25T08:03:24.033Z",
|
|
853
|
+
"type": "click"
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
857
|
+
"questionId": "q1",
|
|
858
|
+
"timestamp": "2026-09-25T08:03:29.705Z",
|
|
859
|
+
"type": "click"
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
863
|
+
"questionId": "q1b",
|
|
864
|
+
"timestamp": "2026-09-25T08:03:33.517Z",
|
|
865
|
+
"type": "click"
|
|
800
866
|
}
|
|
801
867
|
]
|