rushchatbot 1.6.9 → 1.7.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # RushChatbot
2
2
 
3
- **Version:** 1.6.9
3
+ **Version:** 1.7.1
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
 
@@ -1,6 +1,6 @@
1
1
  # RushChatbot
2
2
 
3
- **Version:** 1.6.9
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.6.9
3
+ **Version:** 1.7.1
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rushchatbot",
3
- "version": "1.6.9",
3
+ "version": "1.7.1",
4
4
  "description": "Embeddable hybrid chatbot with n8n fallback, license key protection, and configurable FAQ tree",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -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
  )
@@ -1,7 +1,26 @@
1
1
  import { marked } from 'marked'
2
2
  import type { Message, Question, WidgetSettings } from '../shared/types'
3
3
 
4
- marked.setOptions({ breaks: true })
4
+ marked.use({
5
+ breaks: true,
6
+ renderer: {
7
+ code({ text, lang }: { text: string; lang?: string }): string {
8
+ const escaped = text
9
+ .replace(/&/g, '&amp;')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
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, '&amp;')
18
+ .replace(/</g, '&lt;')
19
+ .replace(/>/g, '&gt;')
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
- const { data } = await axios.post(
228
- webhookUrl,
229
- { query: query.trim(), sessionId: sid },
230
- { timeout: timeoutMs, headers: { 'Content-Type': 'application/json', 'x-session-id': sid } }
231
- )
232
- return res.json({ ...parseN8nResponse(data), source: 'n8n' })
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 = [
@@ -372,6 +375,10 @@ class RushChatbotElement extends HTMLElement {
372
375
  mdDiv.className = 'rushchatbot-md'
373
376
  mdDiv.innerHTML = parseMarkdown(msg.text)
374
377
  bubble.appendChild(mdDiv)
378
+ if (msg.isError) {
379
+ bubble.style.border = '1px solid #EF4444'
380
+ bubble.style.color = '#B91C1C'
381
+ }
375
382
  if (msg.isEndChat) {
376
383
  const newChatBtn = document.createElement('button')
377
384
  newChatBtn.className = 'new-chat-btn'
@@ -195,4 +195,7 @@ const sendBtnStyle = computed(() => ({
195
195
  .rushchatbot-md h1 { font-size: 1.1em; }
196
196
  .rushchatbot-md h2 { font-size: 1em; }
197
197
  .rushchatbot-md h3 { font-size: 0.95em; }
198
+ .rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
199
+ .rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
200
+ .rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
198
201
  </style>
@@ -201,4 +201,7 @@ export default {
201
201
  0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
202
202
  40% { transform: translateY(-6px); opacity: 1; }
203
203
  }
204
+ .rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
205
+ .rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
206
+ .rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
204
207
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rushchatbot",
3
- "version": "1.6.9",
3
+ "version": "1.7.1",
4
4
  "scripts": {
5
5
  "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
6
6
  "dev:client": "vite",
@@ -797,5 +797,35 @@
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"
800
830
  }
801
831
  ]