n8n-chat-pretty 1.0.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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/chat.es.js +205 -0
- package/dist/chat.es.js.map +1 -0
- package/dist/chat.umd.js +2 -0
- package/dist/chat.umd.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types.d.ts +172 -0
- package/dist/types/types.d.ts.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 baufer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# n8n-chat-pretty
|
|
2
|
+
|
|
3
|
+
Drop-in replacement for [@n8n/chat](https://www.npmjs.com/package/@n8n/chat) with message-style conversation experience inspired by [Julian Garnier's work](https://github.com/juliangarnier).
|
|
4
|
+
**[Try Demo](https://chat.baufer.beauty)**
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 💬 **Conversational bubbles** - Messages split naturally into chat-friendly chunks
|
|
11
|
+
- ⌨️ **Typing indicator** - Animated dots between messages with proportional delays
|
|
12
|
+
- 🌙 **Dark mode** - Automatic system preference detection
|
|
13
|
+
- 📱 **Mobile-first** - Responsive design optimized for mobile devices
|
|
14
|
+
- 🎨 **Customizable** - CSS variables for easy theming
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### CDN (Quickest)
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<link href="https://cdn.jsdelivr.net/npm/n8n-chat-pretty/dist/style.css" rel="stylesheet" />
|
|
22
|
+
<script type="module">
|
|
23
|
+
import { createChat } from 'https://cdn.jsdelivr.net/npm/n8n-chat-pretty/dist/chat.es.js';
|
|
24
|
+
|
|
25
|
+
createChat({
|
|
26
|
+
webhookUrl: 'YOUR_N8N_WEBHOOK_URL'
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### npm
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install n8n-chat-pretty
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import 'n8n-chat-pretty/style.css';
|
|
39
|
+
import { createChat } from 'n8n-chat-pretty';
|
|
40
|
+
|
|
41
|
+
createChat({
|
|
42
|
+
webhookUrl: 'YOUR_N8N_WEBHOOK_URL'
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Basic
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<!DOCTYPE html>
|
|
52
|
+
<html>
|
|
53
|
+
<head>
|
|
54
|
+
<link href="https://cdn.jsdelivr.net/npm/n8n-chat-pretty/dist/style.css" rel="stylesheet" />
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
<div id="n8n-chat" style="height: 100vh;"></div>
|
|
58
|
+
|
|
59
|
+
<script type="module">
|
|
60
|
+
import { createChat } from 'https://cdn.jsdelivr.net/npm/n8n-chat-pretty/dist/chat.es.js';
|
|
61
|
+
|
|
62
|
+
createChat({
|
|
63
|
+
webhookUrl: 'YOUR_N8N_WEBHOOK_URL',
|
|
64
|
+
initialMessages: [
|
|
65
|
+
'Hey there! 👋',
|
|
66
|
+
'How can I help you today?'
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
</body>
|
|
71
|
+
</html>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With Options
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
const chat = createChat({
|
|
78
|
+
webhookUrl: 'YOUR_N8N_WEBHOOK_URL',
|
|
79
|
+
target: '#my-chat-container',
|
|
80
|
+
mode: 'fullscreen', // or 'window'
|
|
81
|
+
|
|
82
|
+
initialMessages: [
|
|
83
|
+
'Welcome! 👋',
|
|
84
|
+
'Ask me anything about our services.'
|
|
85
|
+
],
|
|
86
|
+
|
|
87
|
+
theme: {
|
|
88
|
+
primaryColor: '#007bff',
|
|
89
|
+
botMessageBackground: '#f0f0f0',
|
|
90
|
+
userMessageBackground: '#007bff',
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
i18n: {
|
|
94
|
+
inputPlaceholder: 'Ask a question...',
|
|
95
|
+
sendButtonText: 'Send',
|
|
96
|
+
errorMessage: 'Oops! Something went wrong.'
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
typingIndicator: {
|
|
100
|
+
msPerChar: 25, // Typing speed
|
|
101
|
+
baseDelay: 400 // Minimum delay
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
maxBubbleLength: 180, // Max chars before splitting
|
|
105
|
+
|
|
106
|
+
metadata: {
|
|
107
|
+
source: 'website',
|
|
108
|
+
page: window.location.pathname
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Programmatic control
|
|
113
|
+
chat.sendMessage('Hello!');
|
|
114
|
+
chat.clear();
|
|
115
|
+
chat.destroy();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Options
|
|
119
|
+
|
|
120
|
+
| Option | Type | Default | Description |
|
|
121
|
+
|--------|------|---------|-------------|
|
|
122
|
+
| `webhookUrl` | `string` | **required** | Your n8n webhook URL |
|
|
123
|
+
| `target` | `string` | `'#n8n-chat'` | CSS selector for container |
|
|
124
|
+
| `mode` | `'fullscreen' \| 'window'` | `'fullscreen'` | Display mode |
|
|
125
|
+
| `initialMessages` | `string[]` | `['Hey there! 👋', ...]` | Welcome messages |
|
|
126
|
+
| `chatInputKey` | `string` | `'chatInput'` | Key for message in webhook payload |
|
|
127
|
+
| `chatSessionKey` | `string` | `'sessionId'` | Key for session ID |
|
|
128
|
+
| `loadPreviousSession` | `boolean` | `true` | Persist session across reloads |
|
|
129
|
+
| `metadata` | `object` | `{}` | Extra data to send with each message |
|
|
130
|
+
| `theme` | `ThemeOptions` | - | Theme customization |
|
|
131
|
+
| `i18n` | `I18nOptions` | - | Text customization |
|
|
132
|
+
| `typingIndicator` | `object` | `{msPerChar: 20, baseDelay: 300}` | Typing animation speed |
|
|
133
|
+
| `maxBubbleLength` | `number` | `200` | Max chars per bubble |
|
|
134
|
+
| `enableAnimations` | `boolean` | `true` | Enable bubble animations |
|
|
135
|
+
|
|
136
|
+
## Theme Options
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
theme: {
|
|
140
|
+
primaryColor: '#e74266', // Buttons, user messages
|
|
141
|
+
backgroundColor: '#ffffff', // Main background
|
|
142
|
+
textColor: '#000000', // Text color
|
|
143
|
+
botMessageBackground: '#f0f0f0', // Bot bubble background
|
|
144
|
+
userMessageBackground: '#e74266', // User bubble background
|
|
145
|
+
fontFamily: 'system-ui, sans-serif',
|
|
146
|
+
borderRadius: '1.25rem'
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## CSS Variables
|
|
151
|
+
|
|
152
|
+
You can also customize via CSS:
|
|
153
|
+
|
|
154
|
+
```css
|
|
155
|
+
:root {
|
|
156
|
+
--n8n-chat-primary: #e74266;
|
|
157
|
+
--n8n-chat-bg: #ffffff;
|
|
158
|
+
--n8n-chat-text: #000000;
|
|
159
|
+
--n8n-chat-bot-bg: rgba(206, 206, 206, 0.5);
|
|
160
|
+
--n8n-chat-user-bg: #e74266;
|
|
161
|
+
--n8n-chat-user-text: #ffffff;
|
|
162
|
+
--n8n-chat-border-radius: 1.25rem;
|
|
163
|
+
--n8n-chat-font-family: system-ui, sans-serif;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Comparison with @n8n/chat
|
|
168
|
+
|
|
169
|
+
| Feature | @n8n/chat | n8n-chat-pretty |
|
|
170
|
+
|---------|-----------|-----------------|
|
|
171
|
+
| Message splitting | ❌ | ✅ Natural sentence/paragraph splitting |
|
|
172
|
+
| Typing indicator | ⚠️ Single | ✅ Between each bubble |
|
|
173
|
+
| Typing delay | ❌ Fixed | ✅ Proportional to message length |
|
|
174
|
+
| Mobile optimized | ⚠️ Basic | ✅ Mobile-first design |
|
|
175
|
+
|
|
176
|
+
## n8n Workflow Setup
|
|
177
|
+
|
|
178
|
+
1. Create a workflow with **Chat Trigger** node
|
|
179
|
+
2. Add your domain to **Allowed Origins (CORS)**
|
|
180
|
+
3. Connect to your AI agent (OpenAI, Anthropic, etc.)
|
|
181
|
+
4. Use the webhook URL in `createChat()`
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
package/dist/chat.es.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const J = {
|
|
2
|
+
webhookConfig: {
|
|
3
|
+
method: "POST",
|
|
4
|
+
headers: {}
|
|
5
|
+
},
|
|
6
|
+
target: "#n8n-chat",
|
|
7
|
+
mode: "fullscreen",
|
|
8
|
+
initialMessages: [
|
|
9
|
+
"Hey there! 👋",
|
|
10
|
+
"How can I help you today?"
|
|
11
|
+
],
|
|
12
|
+
chatInputKey: "chatInput",
|
|
13
|
+
chatSessionKey: "sessionId",
|
|
14
|
+
loadPreviousSession: !0,
|
|
15
|
+
metadata: {},
|
|
16
|
+
i18n: {
|
|
17
|
+
inputPlaceholder: "Type your message...",
|
|
18
|
+
sendButtonText: "Send",
|
|
19
|
+
errorMessage: "Connection error. Please try again.",
|
|
20
|
+
fallbackResponse: "Sorry, I couldn't process that."
|
|
21
|
+
},
|
|
22
|
+
typingIndicator: {
|
|
23
|
+
msPerChar: 20,
|
|
24
|
+
baseDelay: 300
|
|
25
|
+
},
|
|
26
|
+
maxBubbleLength: 200,
|
|
27
|
+
enableAnimations: !0
|
|
28
|
+
};
|
|
29
|
+
function _(R) {
|
|
30
|
+
var N, B;
|
|
31
|
+
const n = { ...J, ...R };
|
|
32
|
+
if (!n.webhookUrl)
|
|
33
|
+
throw new Error("n8n-chat-pretty: webhookUrl is required");
|
|
34
|
+
function C() {
|
|
35
|
+
return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `sid_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
36
|
+
}
|
|
37
|
+
const S = `n8n-chat-session-${n.chatSessionKey}`;
|
|
38
|
+
let m = n.loadPreviousSession && localStorage.getItem(S) || C();
|
|
39
|
+
n.loadPreviousSession && localStorage.setItem(S, m);
|
|
40
|
+
const v = document.querySelector(n.target);
|
|
41
|
+
if (!v)
|
|
42
|
+
throw new Error(`n8n-chat-pretty: target element "${n.target}" not found`);
|
|
43
|
+
const c = document.createElement("div");
|
|
44
|
+
c.className = `n8n-chat ${n.mode}-mode`;
|
|
45
|
+
const l = document.createElement("div");
|
|
46
|
+
l.className = "n8n-chat-messages";
|
|
47
|
+
const f = document.createElement("div");
|
|
48
|
+
f.className = "n8n-chat-input-area";
|
|
49
|
+
const d = document.createElement("input");
|
|
50
|
+
d.type = "text", d.className = "n8n-chat-input", d.placeholder = ((N = n.i18n) == null ? void 0 : N.inputPlaceholder) || "Type your message...", d.autocomplete = "off";
|
|
51
|
+
const g = document.createElement("button");
|
|
52
|
+
if (g.className = "n8n-chat-send-btn", g.textContent = ((B = n.i18n) == null ? void 0 : B.sendButtonText) || "Send", f.appendChild(d), f.appendChild(g), c.appendChild(l), c.appendChild(f), v.appendChild(c), n.theme) {
|
|
53
|
+
const e = n.theme;
|
|
54
|
+
e.primaryColor && c.style.setProperty("--n8n-chat-primary", e.primaryColor), e.backgroundColor && c.style.setProperty("--n8n-chat-bg", e.backgroundColor), e.textColor && c.style.setProperty("--n8n-chat-text", e.textColor), e.botMessageBackground && c.style.setProperty("--n8n-chat-bot-bg", e.botMessageBackground), e.userMessageBackground && c.style.setProperty("--n8n-chat-user-bg", e.userMessageBackground), e.fontFamily && c.style.setProperty("--n8n-chat-font-family", e.fontFamily), e.borderRadius && c.style.setProperty("--n8n-chat-border-radius", e.borderRadius);
|
|
55
|
+
}
|
|
56
|
+
function E() {
|
|
57
|
+
l.scrollTop = l.scrollHeight;
|
|
58
|
+
}
|
|
59
|
+
function $(e, o) {
|
|
60
|
+
const s = document.createElement("div");
|
|
61
|
+
s.className = `n8n-chat-bubble ${o}`;
|
|
62
|
+
const a = document.createElement("span");
|
|
63
|
+
return a.className = "message", a.textContent = e, s.appendChild(a), s;
|
|
64
|
+
}
|
|
65
|
+
function P() {
|
|
66
|
+
const e = document.createElement("div");
|
|
67
|
+
e.className = "n8n-chat-bubble left";
|
|
68
|
+
const o = document.createElement("span");
|
|
69
|
+
return o.className = "loading", o.innerHTML = "<b>•</b><b>•</b><b>•</b>", e.appendChild(o), e;
|
|
70
|
+
}
|
|
71
|
+
function h(e, o, s = !1) {
|
|
72
|
+
const a = document.createElement("div");
|
|
73
|
+
a.className = `n8n-chat-message-row ${o}`;
|
|
74
|
+
const t = $(e, o);
|
|
75
|
+
return n.enableAnimations && t.classList.add("animate-in"), a.appendChild(t), l.appendChild(a), s && E(), t;
|
|
76
|
+
}
|
|
77
|
+
function U(e) {
|
|
78
|
+
const o = n.maxBubbleLength || 200;
|
|
79
|
+
let s = e.split(/\n\n+/).map((t) => t.trim()).filter((t) => t);
|
|
80
|
+
if (s.length === 1) {
|
|
81
|
+
const t = e.split(/(?=\d+\.\s+\*\*|\n\d+\.\s|\n[-•]\s)/);
|
|
82
|
+
t.length > 1 ? s = t.map((i) => i.trim()).filter((i) => i) : (s = e.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [e], s = s.map((i) => i.trim()).filter((i) => i));
|
|
83
|
+
}
|
|
84
|
+
const a = [];
|
|
85
|
+
for (const t of s)
|
|
86
|
+
if (a.length > 0 && t.length < 30)
|
|
87
|
+
a[a.length - 1] += " " + t;
|
|
88
|
+
else if (t.length > o) {
|
|
89
|
+
const i = t.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [t];
|
|
90
|
+
let r = "";
|
|
91
|
+
for (const u of i)
|
|
92
|
+
r.length + u.length > o && r.length > 0 ? (a.push(r.trim()), r = u) : r += (r ? " " : "") + u.trim();
|
|
93
|
+
r.trim() && a.push(r.trim());
|
|
94
|
+
} else
|
|
95
|
+
a.push(t);
|
|
96
|
+
return a.length > 0 ? a : [e];
|
|
97
|
+
}
|
|
98
|
+
async function O(e, o) {
|
|
99
|
+
const { msPerChar: s = 20, baseDelay: a = 300 } = n.typingIndicator || {};
|
|
100
|
+
for (let t = 0; t < e.length; t++) {
|
|
101
|
+
if (t > 0) {
|
|
102
|
+
const r = document.createElement("div");
|
|
103
|
+
r.className = "n8n-chat-message-row left";
|
|
104
|
+
const u = P();
|
|
105
|
+
r.appendChild(u), l.appendChild(r);
|
|
106
|
+
const p = e[t].length * s + a;
|
|
107
|
+
await new Promise((y) => setTimeout(y, p)), r.remove();
|
|
108
|
+
}
|
|
109
|
+
const i = t === 0;
|
|
110
|
+
h(e[t], o, i);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function H(e) {
|
|
114
|
+
if ((e.headers.get("content-type") || "").includes("application/json"))
|
|
115
|
+
return await e.json();
|
|
116
|
+
const s = await e.text();
|
|
117
|
+
if (!s) return null;
|
|
118
|
+
try {
|
|
119
|
+
return JSON.parse(s);
|
|
120
|
+
} catch {
|
|
121
|
+
return s;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function K(e) {
|
|
125
|
+
var a, t;
|
|
126
|
+
if (typeof e == "string") return e;
|
|
127
|
+
if (!e || typeof e != "object") return ((a = n.i18n) == null ? void 0 : a.fallbackResponse) || "";
|
|
128
|
+
const o = e, s = o.output ?? o.text ?? o.message;
|
|
129
|
+
if (typeof s == "string") return s;
|
|
130
|
+
if (s == null) return ((t = n.i18n) == null ? void 0 : t.fallbackResponse) || "";
|
|
131
|
+
try {
|
|
132
|
+
return JSON.stringify(s);
|
|
133
|
+
} catch {
|
|
134
|
+
return String(s);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function T(e) {
|
|
138
|
+
var a, t, i, r, u;
|
|
139
|
+
const o = document.createElement("div");
|
|
140
|
+
o.className = "n8n-chat-message-row left";
|
|
141
|
+
const s = P();
|
|
142
|
+
o.appendChild(s), l.appendChild(o), E();
|
|
143
|
+
try {
|
|
144
|
+
const p = {
|
|
145
|
+
action: "sendMessage",
|
|
146
|
+
[n.chatSessionKey]: m,
|
|
147
|
+
[n.chatInputKey]: e,
|
|
148
|
+
...n.metadata
|
|
149
|
+
}, y = (((a = n.webhookConfig) == null ? void 0 : a.method) || "POST").toUpperCase(), w = {
|
|
150
|
+
...(t = n.webhookConfig) == null ? void 0 : t.headers
|
|
151
|
+
};
|
|
152
|
+
let k = n.webhookUrl;
|
|
153
|
+
const x = { method: y, headers: w };
|
|
154
|
+
if (y === "GET") {
|
|
155
|
+
const L = new URL(k, window.location.href);
|
|
156
|
+
for (const [A, b] of Object.entries(p))
|
|
157
|
+
b != null && L.searchParams.set(A, typeof b == "string" ? b : JSON.stringify(b));
|
|
158
|
+
k = L.toString();
|
|
159
|
+
} else
|
|
160
|
+
w["Content-Type"] = w["Content-Type"] || "application/json", x.body = JSON.stringify(p);
|
|
161
|
+
const M = await fetch(k, x), j = await H(M);
|
|
162
|
+
if (o.remove(), !M.ok) {
|
|
163
|
+
h(((i = n.i18n) == null ? void 0 : i.errorMessage) || `Request failed (${M.status})`, "left", !0);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const D = K(j) || ((r = n.i18n) == null ? void 0 : r.fallbackResponse) || "", q = U(D);
|
|
167
|
+
await O(q, "left");
|
|
168
|
+
} catch {
|
|
169
|
+
o.remove(), h(((u = n.i18n) == null ? void 0 : u.errorMessage) || "Connection error.", "left", !0);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function I() {
|
|
173
|
+
const e = d.value.trim();
|
|
174
|
+
e && (d.value = "", h(e, "right", !0), T(e));
|
|
175
|
+
}
|
|
176
|
+
if (g.addEventListener("click", I), d.addEventListener("keydown", (e) => {
|
|
177
|
+
e.key === "Enter" && I();
|
|
178
|
+
}), n.initialMessages && n.initialMessages.length > 0) {
|
|
179
|
+
let e = 300;
|
|
180
|
+
for (const o of n.initialMessages)
|
|
181
|
+
setTimeout(() => h(o, "left", !0), e), e += 700;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
sendMessage: async (e) => {
|
|
185
|
+
h(e, "right", !0), await T(e);
|
|
186
|
+
},
|
|
187
|
+
addMessage: (e, o) => {
|
|
188
|
+
h(e, o, !0);
|
|
189
|
+
},
|
|
190
|
+
clear: () => {
|
|
191
|
+
l.innerHTML = "";
|
|
192
|
+
},
|
|
193
|
+
destroy: () => {
|
|
194
|
+
c.remove();
|
|
195
|
+
},
|
|
196
|
+
getSessionId: () => m,
|
|
197
|
+
resetSession: () => {
|
|
198
|
+
m = C(), n.loadPreviousSession && localStorage.setItem(S, m);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
_ as createChat
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=chat.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.es.js","sources":["../src/index.ts"],"sourcesContent":["import './styles.css';\nimport type { ChatOptions, ChatInstance } from './types';\n\n// Default options matching n8n/chat API\nconst defaultOptions: Partial<ChatOptions> = {\n webhookConfig: {\n method: 'POST',\n headers: {}\n },\n target: '#n8n-chat',\n mode: 'fullscreen',\n initialMessages: [\n 'Hey there! 👋',\n 'How can I help you today?'\n ],\n chatInputKey: 'chatInput',\n chatSessionKey: 'sessionId',\n loadPreviousSession: true,\n metadata: {},\n i18n: {\n inputPlaceholder: 'Type your message...',\n sendButtonText: 'Send',\n errorMessage: 'Connection error. Please try again.',\n fallbackResponse: \"Sorry, I couldn't process that.\"\n },\n typingIndicator: {\n msPerChar: 20,\n baseDelay: 300\n },\n maxBubbleLength: 200,\n enableAnimations: true\n};\n\n/**\n * Creates a new chat instance\n */\nexport function createChat(options: ChatOptions): ChatInstance {\n const config = { ...defaultOptions, ...options };\n \n // Validate required options\n if (!config.webhookUrl) {\n throw new Error('n8n-chat-pretty: webhookUrl is required');\n }\n\n function generateSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for older browsers/webviews\n return `sid_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;\n }\n\n // Get or create session ID\n const sessionKey = `n8n-chat-session-${config.chatSessionKey}`;\n let sessionId = config.loadPreviousSession\n ? (localStorage.getItem(sessionKey) || generateSessionId())\n : generateSessionId();\n\n if (config.loadPreviousSession) {\n localStorage.setItem(sessionKey, sessionId);\n }\n\n // Get target element\n const targetEl = document.querySelector(config.target!) as HTMLElement;\n if (!targetEl) {\n throw new Error(`n8n-chat-pretty: target element \"${config.target}\" not found`);\n }\n\n // Create chat structure\n const container = document.createElement('div');\n container.className = `n8n-chat ${config.mode}-mode`;\n \n const messagesEl = document.createElement('div');\n messagesEl.className = 'n8n-chat-messages';\n \n const inputArea = document.createElement('div');\n inputArea.className = 'n8n-chat-input-area';\n \n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'n8n-chat-input';\n input.placeholder = config.i18n?.inputPlaceholder || 'Type your message...';\n input.autocomplete = 'off';\n \n const sendBtn = document.createElement('button');\n sendBtn.className = 'n8n-chat-send-btn';\n sendBtn.textContent = config.i18n?.sendButtonText || 'Send';\n \n inputArea.appendChild(input);\n inputArea.appendChild(sendBtn);\n container.appendChild(messagesEl);\n container.appendChild(inputArea);\n targetEl.appendChild(container);\n\n // Apply theme\n if (config.theme) {\n const t = config.theme;\n if (t.primaryColor) container.style.setProperty('--n8n-chat-primary', t.primaryColor);\n if (t.backgroundColor) container.style.setProperty('--n8n-chat-bg', t.backgroundColor);\n if (t.textColor) container.style.setProperty('--n8n-chat-text', t.textColor);\n if (t.botMessageBackground) container.style.setProperty('--n8n-chat-bot-bg', t.botMessageBackground);\n if (t.userMessageBackground) container.style.setProperty('--n8n-chat-user-bg', t.userMessageBackground);\n if (t.fontFamily) container.style.setProperty('--n8n-chat-font-family', t.fontFamily);\n if (t.borderRadius) container.style.setProperty('--n8n-chat-border-radius', t.borderRadius);\n }\n\n // Helper functions\n function scrollToBottom() {\n messagesEl.scrollTop = messagesEl.scrollHeight;\n }\n\n function createBubble(text: string, position: 'left' | 'right'): HTMLElement {\n const bubble = document.createElement('div');\n bubble.className = `n8n-chat-bubble ${position}`;\n const msg = document.createElement('span');\n msg.className = 'message';\n // Use textContent to avoid XSS via untrusted webhook/user content\n msg.textContent = text;\n bubble.appendChild(msg);\n return bubble;\n }\n\n function createLoadingBubble(): HTMLElement {\n const bubble = document.createElement('div');\n bubble.className = 'n8n-chat-bubble left';\n const loading = document.createElement('span');\n loading.className = 'loading';\n loading.innerHTML = '<b>•</b><b>•</b><b>•</b>';\n bubble.appendChild(loading);\n return bubble;\n }\n\n function addMessage(text: string, position: 'left' | 'right', shouldScroll = false): HTMLElement {\n const row = document.createElement('div');\n row.className = `n8n-chat-message-row ${position}`;\n const bubble = createBubble(text, position);\n \n if (config.enableAnimations) {\n bubble.classList.add('animate-in');\n }\n \n row.appendChild(bubble);\n messagesEl.appendChild(row);\n \n if (shouldScroll) {\n scrollToBottom();\n }\n \n return bubble;\n }\n\n function splitIntoChunks(text: string): string[] {\n const MAX_LENGTH = config.maxBubbleLength || 200;\n \n // First try splitting by double newlines (paragraphs)\n let chunks = text.split(/\\n\\n+/).map(s => s.trim()).filter(s => s);\n \n // If only one chunk, try other split strategies\n if (chunks.length === 1) {\n // Try splitting by numbered lists or markdown headers\n const listSplit = text.split(/(?=\\d+\\.\\s+\\*\\*|\\n\\d+\\.\\s|\\n[-•]\\s)/);\n if (listSplit.length > 1) {\n chunks = listSplit.map(s => s.trim()).filter(s => s);\n } else {\n // Split by sentence endings\n chunks = text.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [text];\n chunks = chunks.map(s => s.trim()).filter(s => s);\n }\n }\n \n // Merge very short chunks, split very long ones\n const result: string[] = [];\n for (const chunk of chunks) {\n if (result.length > 0 && chunk.length < 30) {\n result[result.length - 1] += ' ' + chunk;\n } else if (chunk.length > MAX_LENGTH) {\n const sentences = chunk.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [chunk];\n let current = '';\n for (const sentence of sentences) {\n if (current.length + sentence.length > MAX_LENGTH && current.length > 0) {\n result.push(current.trim());\n current = sentence;\n } else {\n current += (current ? ' ' : '') + sentence.trim();\n }\n }\n if (current.trim()) result.push(current.trim());\n } else {\n result.push(chunk);\n }\n }\n \n return result.length > 0 ? result : [text];\n }\n\n async function addMessagesSequentially(chunks: string[], position: 'left' | 'right') {\n const { msPerChar = 20, baseDelay = 300 } = config.typingIndicator || {};\n \n for (let i = 0; i < chunks.length; i++) {\n if (i > 0) {\n const typingRow = document.createElement('div');\n typingRow.className = 'n8n-chat-message-row left';\n const typingBubble = createLoadingBubble();\n typingRow.appendChild(typingBubble);\n messagesEl.appendChild(typingRow);\n \n const delay = chunks[i].length * msPerChar + baseDelay;\n await new Promise(r => setTimeout(r, delay));\n \n typingRow.remove();\n }\n \n const shouldScroll = (i === 0);\n addMessage(chunks[i], position, shouldScroll);\n }\n }\n\n async function parseWebhookResponse(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n return await response.json();\n }\n const text = await response.text();\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n\n function extractBotMessage(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return config.i18n?.fallbackResponse || '';\n\n const record = data as Record<string, unknown>;\n const candidate = record.output ?? record.text ?? record.message;\n if (typeof candidate === 'string') return candidate;\n if (candidate == null) return config.i18n?.fallbackResponse || '';\n try {\n return JSON.stringify(candidate);\n } catch {\n return String(candidate);\n }\n }\n\n async function sendToWebhook(message: string) {\n const row = document.createElement('div');\n row.className = 'n8n-chat-message-row left';\n const loadingBubble = createLoadingBubble();\n row.appendChild(loadingBubble);\n messagesEl.appendChild(row);\n scrollToBottom();\n\n try {\n const payload = {\n action: 'sendMessage',\n [config.chatSessionKey!]: sessionId,\n [config.chatInputKey!]: message,\n ...config.metadata\n };\n\n const method = (config.webhookConfig?.method || 'POST').toUpperCase();\n const headers: Record<string, string> = {\n ...config.webhookConfig?.headers\n };\n\n let url = config.webhookUrl;\n const init: RequestInit = { method, headers };\n\n if (method === 'GET') {\n const u = new URL(url, window.location.href);\n for (const [key, value] of Object.entries(payload)) {\n if (value == null) continue;\n u.searchParams.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n url = u.toString();\n } else {\n headers['Content-Type'] = headers['Content-Type'] || 'application/json';\n init.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, init);\n const data = await parseWebhookResponse(response);\n row.remove();\n\n if (!response.ok) {\n addMessage(config.i18n?.errorMessage || `Request failed (${response.status})`, 'left', true);\n return;\n }\n\n const botMessage = extractBotMessage(data) || config.i18n?.fallbackResponse || '';\n const chunks = splitIntoChunks(botMessage);\n await addMessagesSequentially(chunks, 'left');\n \n } catch (error) {\n row.remove();\n addMessage(config.i18n?.errorMessage || 'Connection error.', 'left', true);\n }\n }\n\n function handleSend() {\n const text = input.value.trim();\n if (!text) return;\n \n input.value = '';\n addMessage(text, 'right', true);\n sendToWebhook(text);\n }\n\n // Event listeners\n sendBtn.addEventListener('click', handleSend);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') handleSend();\n });\n\n // Initial messages\n if (config.initialMessages && config.initialMessages.length > 0) {\n let delay = 300;\n for (const msg of config.initialMessages) {\n setTimeout(() => addMessage(msg, 'left', true), delay);\n delay += 700;\n }\n }\n\n // Return chat instance\n return {\n sendMessage: async (text: string) => {\n addMessage(text, 'right', true);\n await sendToWebhook(text);\n },\n addMessage: (text: string, position: 'left' | 'right') => {\n addMessage(text, position, true);\n },\n clear: () => {\n messagesEl.innerHTML = '';\n },\n destroy: () => {\n container.remove();\n },\n getSessionId: () => sessionId,\n resetSession: () => {\n sessionId = generateSessionId();\n if (config.loadPreviousSession) {\n localStorage.setItem(sessionKey, sessionId);\n }\n }\n };\n}\n\n// Re-export types\nexport type { ChatOptions, ChatInstance, ThemeOptions, I18nOptions } from './types';\n"],"names":["defaultOptions","createChat","options","_a","_b","config","generateSessionId","sessionKey","sessionId","targetEl","container","messagesEl","inputArea","input","sendBtn","t","scrollToBottom","createBubble","text","position","bubble","msg","createLoadingBubble","loading","addMessage","shouldScroll","row","splitIntoChunks","MAX_LENGTH","chunks","s","listSplit","result","chunk","sentences","current","sentence","addMessagesSequentially","msPerChar","baseDelay","i","typingRow","typingBubble","delay","r","parseWebhookResponse","response","extractBotMessage","data","record","candidate","sendToWebhook","message","_c","_d","_e","loadingBubble","payload","method","headers","url","init","u","key","value","botMessage","handleSend"],"mappings":"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"}
|
package/dist/chat.umd.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(h,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(h=typeof globalThis<"u"?globalThis:h||self,p(h.N8nChatPretty={}))})(this,function(h){"use strict";const p={webhookConfig:{method:"POST",headers:{}},target:"#n8n-chat",mode:"fullscreen",initialMessages:["Hey there! 👋","How can I help you today?"],chatInputKey:"chatInput",chatSessionKey:"sessionId",loadPreviousSession:!0,metadata:{},i18n:{inputPlaceholder:"Type your message...",sendButtonText:"Send",errorMessage:"Connection error. Please try again.",fallbackResponse:"Sorry, I couldn't process that."},typingIndicator:{msPerChar:20,baseDelay:300},maxBubbleLength:200,enableAnimations:!0};function O(U){var x,L;const n={...p,...U};if(!n.webhookUrl)throw new Error("n8n-chat-pretty: webhookUrl is required");function w(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`sid_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`}const k=`n8n-chat-session-${n.chatSessionKey}`;let m=n.loadPreviousSession&&localStorage.getItem(k)||w();n.loadPreviousSession&&localStorage.setItem(k,m);const T=document.querySelector(n.target);if(!T)throw new Error(`n8n-chat-pretty: target element "${n.target}" not found`);const c=document.createElement("div");c.className=`n8n-chat ${n.mode}-mode`;const l=document.createElement("div");l.className="n8n-chat-messages";const y=document.createElement("div");y.className="n8n-chat-input-area";const d=document.createElement("input");d.type="text",d.className="n8n-chat-input",d.placeholder=((x=n.i18n)==null?void 0:x.inputPlaceholder)||"Type your message...",d.autocomplete="off";const b=document.createElement("button");if(b.className="n8n-chat-send-btn",b.textContent=((L=n.i18n)==null?void 0:L.sendButtonText)||"Send",y.appendChild(d),y.appendChild(b),c.appendChild(l),c.appendChild(y),T.appendChild(c),n.theme){const e=n.theme;e.primaryColor&&c.style.setProperty("--n8n-chat-primary",e.primaryColor),e.backgroundColor&&c.style.setProperty("--n8n-chat-bg",e.backgroundColor),e.textColor&&c.style.setProperty("--n8n-chat-text",e.textColor),e.botMessageBackground&&c.style.setProperty("--n8n-chat-bot-bg",e.botMessageBackground),e.userMessageBackground&&c.style.setProperty("--n8n-chat-user-bg",e.userMessageBackground),e.fontFamily&&c.style.setProperty("--n8n-chat-font-family",e.fontFamily),e.borderRadius&&c.style.setProperty("--n8n-chat-border-radius",e.borderRadius)}function E(){l.scrollTop=l.scrollHeight}function j(e,o){const s=document.createElement("div");s.className=`n8n-chat-bubble ${o}`;const a=document.createElement("span");return a.className="message",a.textContent=e,s.appendChild(a),s}function N(){const e=document.createElement("div");e.className="n8n-chat-bubble left";const o=document.createElement("span");return o.className="loading",o.innerHTML="<b>•</b><b>•</b><b>•</b>",e.appendChild(o),e}function f(e,o,s=!1){const a=document.createElement("div");a.className=`n8n-chat-message-row ${o}`;const t=j(e,o);return n.enableAnimations&&t.classList.add("animate-in"),a.appendChild(t),l.appendChild(a),s&&E(),t}function H(e){const o=n.maxBubbleLength||200;let s=e.split(/\n\n+/).map(t=>t.trim()).filter(t=>t);if(s.length===1){const t=e.split(/(?=\d+\.\s+\*\*|\n\d+\.\s|\n[-•]\s)/);t.length>1?s=t.map(i=>i.trim()).filter(i=>i):(s=e.match(/[^.!?]+[.!?]+|[^.!?]+$/g)||[e],s=s.map(i=>i.trim()).filter(i=>i))}const a=[];for(const t of s)if(a.length>0&&t.length<30)a[a.length-1]+=" "+t;else if(t.length>o){const i=t.match(/[^.!?]+[.!?]+|[^.!?]+$/g)||[t];let r="";for(const u of i)r.length+u.length>o&&r.length>0?(a.push(r.trim()),r=u):r+=(r?" ":"")+u.trim();r.trim()&&a.push(r.trim())}else a.push(t);return a.length>0?a:[e]}async function K(e,o){const{msPerChar:s=20,baseDelay:a=300}=n.typingIndicator||{};for(let t=0;t<e.length;t++){if(t>0){const r=document.createElement("div");r.className="n8n-chat-message-row left";const u=N();r.appendChild(u),l.appendChild(r);const g=e[t].length*s+a;await new Promise(C=>setTimeout(C,g)),r.remove()}const i=t===0;f(e[t],o,i)}}async function D(e){if((e.headers.get("content-type")||"").includes("application/json"))return await e.json();const s=await e.text();if(!s)return null;try{return JSON.parse(s)}catch{return s}}function q(e){var a,t;if(typeof e=="string")return e;if(!e||typeof e!="object")return((a=n.i18n)==null?void 0:a.fallbackResponse)||"";const o=e,s=o.output??o.text??o.message;if(typeof s=="string")return s;if(s==null)return((t=n.i18n)==null?void 0:t.fallbackResponse)||"";try{return JSON.stringify(s)}catch{return String(s)}}async function I(e){var a,t,i,r,u;const o=document.createElement("div");o.className="n8n-chat-message-row left";const s=N();o.appendChild(s),l.appendChild(o),E();try{const g={action:"sendMessage",[n.chatSessionKey]:m,[n.chatInputKey]:e,...n.metadata},C=(((a=n.webhookConfig)==null?void 0:a.method)||"POST").toUpperCase(),M={...(t=n.webhookConfig)==null?void 0:t.headers};let v=n.webhookUrl;const R={method:C,headers:M};if(C==="GET"){const $=new URL(v,window.location.href);for(const[F,S]of Object.entries(g))S!=null&&$.searchParams.set(F,typeof S=="string"?S:JSON.stringify(S));v=$.toString()}else M["Content-Type"]=M["Content-Type"]||"application/json",R.body=JSON.stringify(g);const P=await fetch(v,R),A=await D(P);if(o.remove(),!P.ok){f(((i=n.i18n)==null?void 0:i.errorMessage)||`Request failed (${P.status})`,"left",!0);return}const J=q(A)||((r=n.i18n)==null?void 0:r.fallbackResponse)||"",_=H(J);await K(_,"left")}catch{o.remove(),f(((u=n.i18n)==null?void 0:u.errorMessage)||"Connection error.","left",!0)}}function B(){const e=d.value.trim();e&&(d.value="",f(e,"right",!0),I(e))}if(b.addEventListener("click",B),d.addEventListener("keydown",e=>{e.key==="Enter"&&B()}),n.initialMessages&&n.initialMessages.length>0){let e=300;for(const o of n.initialMessages)setTimeout(()=>f(o,"left",!0),e),e+=700}return{sendMessage:async e=>{f(e,"right",!0),await I(e)},addMessage:(e,o)=>{f(e,o,!0)},clear:()=>{l.innerHTML=""},destroy:()=>{c.remove()},getSessionId:()=>m,resetSession:()=>{m=w(),n.loadPreviousSession&&localStorage.setItem(k,m)}}}h.createChat=O,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
|
|
2
|
+
//# sourceMappingURL=chat.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.umd.js","sources":["../src/index.ts"],"sourcesContent":["import './styles.css';\nimport type { ChatOptions, ChatInstance } from './types';\n\n// Default options matching n8n/chat API\nconst defaultOptions: Partial<ChatOptions> = {\n webhookConfig: {\n method: 'POST',\n headers: {}\n },\n target: '#n8n-chat',\n mode: 'fullscreen',\n initialMessages: [\n 'Hey there! 👋',\n 'How can I help you today?'\n ],\n chatInputKey: 'chatInput',\n chatSessionKey: 'sessionId',\n loadPreviousSession: true,\n metadata: {},\n i18n: {\n inputPlaceholder: 'Type your message...',\n sendButtonText: 'Send',\n errorMessage: 'Connection error. Please try again.',\n fallbackResponse: \"Sorry, I couldn't process that.\"\n },\n typingIndicator: {\n msPerChar: 20,\n baseDelay: 300\n },\n maxBubbleLength: 200,\n enableAnimations: true\n};\n\n/**\n * Creates a new chat instance\n */\nexport function createChat(options: ChatOptions): ChatInstance {\n const config = { ...defaultOptions, ...options };\n \n // Validate required options\n if (!config.webhookUrl) {\n throw new Error('n8n-chat-pretty: webhookUrl is required');\n }\n\n function generateSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for older browsers/webviews\n return `sid_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;\n }\n\n // Get or create session ID\n const sessionKey = `n8n-chat-session-${config.chatSessionKey}`;\n let sessionId = config.loadPreviousSession\n ? (localStorage.getItem(sessionKey) || generateSessionId())\n : generateSessionId();\n\n if (config.loadPreviousSession) {\n localStorage.setItem(sessionKey, sessionId);\n }\n\n // Get target element\n const targetEl = document.querySelector(config.target!) as HTMLElement;\n if (!targetEl) {\n throw new Error(`n8n-chat-pretty: target element \"${config.target}\" not found`);\n }\n\n // Create chat structure\n const container = document.createElement('div');\n container.className = `n8n-chat ${config.mode}-mode`;\n \n const messagesEl = document.createElement('div');\n messagesEl.className = 'n8n-chat-messages';\n \n const inputArea = document.createElement('div');\n inputArea.className = 'n8n-chat-input-area';\n \n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'n8n-chat-input';\n input.placeholder = config.i18n?.inputPlaceholder || 'Type your message...';\n input.autocomplete = 'off';\n \n const sendBtn = document.createElement('button');\n sendBtn.className = 'n8n-chat-send-btn';\n sendBtn.textContent = config.i18n?.sendButtonText || 'Send';\n \n inputArea.appendChild(input);\n inputArea.appendChild(sendBtn);\n container.appendChild(messagesEl);\n container.appendChild(inputArea);\n targetEl.appendChild(container);\n\n // Apply theme\n if (config.theme) {\n const t = config.theme;\n if (t.primaryColor) container.style.setProperty('--n8n-chat-primary', t.primaryColor);\n if (t.backgroundColor) container.style.setProperty('--n8n-chat-bg', t.backgroundColor);\n if (t.textColor) container.style.setProperty('--n8n-chat-text', t.textColor);\n if (t.botMessageBackground) container.style.setProperty('--n8n-chat-bot-bg', t.botMessageBackground);\n if (t.userMessageBackground) container.style.setProperty('--n8n-chat-user-bg', t.userMessageBackground);\n if (t.fontFamily) container.style.setProperty('--n8n-chat-font-family', t.fontFamily);\n if (t.borderRadius) container.style.setProperty('--n8n-chat-border-radius', t.borderRadius);\n }\n\n // Helper functions\n function scrollToBottom() {\n messagesEl.scrollTop = messagesEl.scrollHeight;\n }\n\n function createBubble(text: string, position: 'left' | 'right'): HTMLElement {\n const bubble = document.createElement('div');\n bubble.className = `n8n-chat-bubble ${position}`;\n const msg = document.createElement('span');\n msg.className = 'message';\n // Use textContent to avoid XSS via untrusted webhook/user content\n msg.textContent = text;\n bubble.appendChild(msg);\n return bubble;\n }\n\n function createLoadingBubble(): HTMLElement {\n const bubble = document.createElement('div');\n bubble.className = 'n8n-chat-bubble left';\n const loading = document.createElement('span');\n loading.className = 'loading';\n loading.innerHTML = '<b>•</b><b>•</b><b>•</b>';\n bubble.appendChild(loading);\n return bubble;\n }\n\n function addMessage(text: string, position: 'left' | 'right', shouldScroll = false): HTMLElement {\n const row = document.createElement('div');\n row.className = `n8n-chat-message-row ${position}`;\n const bubble = createBubble(text, position);\n \n if (config.enableAnimations) {\n bubble.classList.add('animate-in');\n }\n \n row.appendChild(bubble);\n messagesEl.appendChild(row);\n \n if (shouldScroll) {\n scrollToBottom();\n }\n \n return bubble;\n }\n\n function splitIntoChunks(text: string): string[] {\n const MAX_LENGTH = config.maxBubbleLength || 200;\n \n // First try splitting by double newlines (paragraphs)\n let chunks = text.split(/\\n\\n+/).map(s => s.trim()).filter(s => s);\n \n // If only one chunk, try other split strategies\n if (chunks.length === 1) {\n // Try splitting by numbered lists or markdown headers\n const listSplit = text.split(/(?=\\d+\\.\\s+\\*\\*|\\n\\d+\\.\\s|\\n[-•]\\s)/);\n if (listSplit.length > 1) {\n chunks = listSplit.map(s => s.trim()).filter(s => s);\n } else {\n // Split by sentence endings\n chunks = text.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [text];\n chunks = chunks.map(s => s.trim()).filter(s => s);\n }\n }\n \n // Merge very short chunks, split very long ones\n const result: string[] = [];\n for (const chunk of chunks) {\n if (result.length > 0 && chunk.length < 30) {\n result[result.length - 1] += ' ' + chunk;\n } else if (chunk.length > MAX_LENGTH) {\n const sentences = chunk.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [chunk];\n let current = '';\n for (const sentence of sentences) {\n if (current.length + sentence.length > MAX_LENGTH && current.length > 0) {\n result.push(current.trim());\n current = sentence;\n } else {\n current += (current ? ' ' : '') + sentence.trim();\n }\n }\n if (current.trim()) result.push(current.trim());\n } else {\n result.push(chunk);\n }\n }\n \n return result.length > 0 ? result : [text];\n }\n\n async function addMessagesSequentially(chunks: string[], position: 'left' | 'right') {\n const { msPerChar = 20, baseDelay = 300 } = config.typingIndicator || {};\n \n for (let i = 0; i < chunks.length; i++) {\n if (i > 0) {\n const typingRow = document.createElement('div');\n typingRow.className = 'n8n-chat-message-row left';\n const typingBubble = createLoadingBubble();\n typingRow.appendChild(typingBubble);\n messagesEl.appendChild(typingRow);\n \n const delay = chunks[i].length * msPerChar + baseDelay;\n await new Promise(r => setTimeout(r, delay));\n \n typingRow.remove();\n }\n \n const shouldScroll = (i === 0);\n addMessage(chunks[i], position, shouldScroll);\n }\n }\n\n async function parseWebhookResponse(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n return await response.json();\n }\n const text = await response.text();\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n\n function extractBotMessage(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return config.i18n?.fallbackResponse || '';\n\n const record = data as Record<string, unknown>;\n const candidate = record.output ?? record.text ?? record.message;\n if (typeof candidate === 'string') return candidate;\n if (candidate == null) return config.i18n?.fallbackResponse || '';\n try {\n return JSON.stringify(candidate);\n } catch {\n return String(candidate);\n }\n }\n\n async function sendToWebhook(message: string) {\n const row = document.createElement('div');\n row.className = 'n8n-chat-message-row left';\n const loadingBubble = createLoadingBubble();\n row.appendChild(loadingBubble);\n messagesEl.appendChild(row);\n scrollToBottom();\n\n try {\n const payload = {\n action: 'sendMessage',\n [config.chatSessionKey!]: sessionId,\n [config.chatInputKey!]: message,\n ...config.metadata\n };\n\n const method = (config.webhookConfig?.method || 'POST').toUpperCase();\n const headers: Record<string, string> = {\n ...config.webhookConfig?.headers\n };\n\n let url = config.webhookUrl;\n const init: RequestInit = { method, headers };\n\n if (method === 'GET') {\n const u = new URL(url, window.location.href);\n for (const [key, value] of Object.entries(payload)) {\n if (value == null) continue;\n u.searchParams.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n url = u.toString();\n } else {\n headers['Content-Type'] = headers['Content-Type'] || 'application/json';\n init.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, init);\n const data = await parseWebhookResponse(response);\n row.remove();\n\n if (!response.ok) {\n addMessage(config.i18n?.errorMessage || `Request failed (${response.status})`, 'left', true);\n return;\n }\n\n const botMessage = extractBotMessage(data) || config.i18n?.fallbackResponse || '';\n const chunks = splitIntoChunks(botMessage);\n await addMessagesSequentially(chunks, 'left');\n \n } catch (error) {\n row.remove();\n addMessage(config.i18n?.errorMessage || 'Connection error.', 'left', true);\n }\n }\n\n function handleSend() {\n const text = input.value.trim();\n if (!text) return;\n \n input.value = '';\n addMessage(text, 'right', true);\n sendToWebhook(text);\n }\n\n // Event listeners\n sendBtn.addEventListener('click', handleSend);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') handleSend();\n });\n\n // Initial messages\n if (config.initialMessages && config.initialMessages.length > 0) {\n let delay = 300;\n for (const msg of config.initialMessages) {\n setTimeout(() => addMessage(msg, 'left', true), delay);\n delay += 700;\n }\n }\n\n // Return chat instance\n return {\n sendMessage: async (text: string) => {\n addMessage(text, 'right', true);\n await sendToWebhook(text);\n },\n addMessage: (text: string, position: 'left' | 'right') => {\n addMessage(text, position, true);\n },\n clear: () => {\n messagesEl.innerHTML = '';\n },\n destroy: () => {\n container.remove();\n },\n getSessionId: () => sessionId,\n resetSession: () => {\n sessionId = generateSessionId();\n if (config.loadPreviousSession) {\n localStorage.setItem(sessionKey, sessionId);\n }\n }\n };\n}\n\n// Re-export types\nexport type { ChatOptions, ChatInstance, ThemeOptions, I18nOptions } from './types';\n"],"names":["defaultOptions","createChat","options","config","generateSessionId","sessionKey","sessionId","targetEl","container","messagesEl","inputArea","input","_a","sendBtn","_b","t","scrollToBottom","createBubble","text","position","bubble","msg","createLoadingBubble","loading","addMessage","shouldScroll","row","splitIntoChunks","MAX_LENGTH","chunks","s","listSplit","result","chunk","sentences","current","sentence","addMessagesSequentially","msPerChar","baseDelay","i","typingRow","typingBubble","delay","r","parseWebhookResponse","response","extractBotMessage","data","record","candidate","sendToWebhook","message","loadingBubble","payload","method","headers","url","init","u","key","value","_c","botMessage","_d","_e","handleSend"],"mappings":"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"}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--n8n-chat-primary: #e74266;--n8n-chat-primary-hover: #d63a5c;--n8n-chat-bg: #FFF;--n8n-chat-bg-secondary: rgba(206,206,206,.5);--n8n-chat-text: #000;--n8n-chat-text-secondary: rgba(0,0,0,.5);--n8n-chat-user-bg: var(--n8n-chat-primary);--n8n-chat-user-text: #fff;--n8n-chat-bot-bg: var(--n8n-chat-bg-secondary);--n8n-chat-bot-text: var(--n8n-chat-text);--n8n-chat-border: rgba(206,206,206,.5);--n8n-chat-font-family: system-ui, -apple-system, sans-serif;--n8n-chat-font-size: 1rem;--n8n-chat-spacing: 1rem;--n8n-chat-border-radius: 1.25rem;--n8n-chat-window-width: 400px;--n8n-chat-window-height: 600px}@media (prefers-color-scheme: dark){:root{--n8n-chat-bg: #0a0a0a;--n8n-chat-bg-secondary: rgba(206,206,206,.125);--n8n-chat-text: #FFF;--n8n-chat-text-secondary: rgba(206,206,206,.5);--n8n-chat-border: rgba(206,206,206,.2)}}.n8n-chat{display:flex;flex-direction:column;height:100%;width:100%;background:var(--n8n-chat-bg);color:var(--n8n-chat-text);font-family:var(--n8n-chat-font-family);font-size:var(--n8n-chat-font-size);position:relative;overflow:hidden}.n8n-chat *,.n8n-chat *:before,.n8n-chat *:after{box-sizing:border-box;margin:0;padding:0}.n8n-chat-messages{flex:1;overflow-y:auto;overflow-x:hidden;padding:var(--n8n-chat-spacing);display:flex;flex-direction:column;gap:.25rem}.n8n-chat-message-row{display:flex;margin-bottom:.25rem}.n8n-chat-message-row.right{justify-content:flex-end}.n8n-chat-bubble{display:inline-block;padding:.5rem .85rem;line-height:1.3;border-radius:var(--n8n-chat-border-radius);max-width:85%;word-wrap:break-word}.n8n-chat-bubble.left{background:var(--n8n-chat-bot-bg);color:var(--n8n-chat-bot-text);border-bottom-left-radius:0}.n8n-chat-bubble.right{background:var(--n8n-chat-user-bg);color:var(--n8n-chat-user-text);border-bottom-right-radius:0}.n8n-chat-bubble .message{display:inline}.n8n-chat-bubble .loading{white-space:pre;font-size:1.5rem;line-height:1rem}.n8n-chat-bubble .loading b{display:inline-block;color:var(--n8n-chat-text-secondary);animation:n8n-chat-pulse .6s infinite alternate}.n8n-chat-bubble .loading b:nth-child(2){animation-delay:.1s}.n8n-chat-bubble .loading b:nth-child(3){animation-delay:.2s}@keyframes n8n-chat-pulse{to{opacity:.3;transform:scale(.8)}}.n8n-chat-input-area{display:flex;gap:.5rem;padding:var(--n8n-chat-spacing);background:var(--n8n-chat-bg);border-top:1px solid var(--n8n-chat-border);flex-shrink:0}.n8n-chat-input{flex:1;padding:.6rem 1rem;border:1px solid var(--n8n-chat-border);border-radius:1.5rem;font-size:1rem;font-family:inherit;background:var(--n8n-chat-bg);color:var(--n8n-chat-text);outline:none;transition:border-color .2s}.n8n-chat-input:focus{border-color:var(--n8n-chat-primary)}.n8n-chat-input::placeholder{color:var(--n8n-chat-text-secondary)}.n8n-chat-send-btn{padding:.6rem 1.2rem;border:none;border-radius:1.5rem;background:var(--n8n-chat-primary);color:#fff;font-size:1rem;font-family:inherit;cursor:pointer;transition:background-color .2s}.n8n-chat-send-btn:hover{background:var(--n8n-chat-primary-hover)}.n8n-chat-send-btn:disabled{opacity:.5;cursor:not-allowed}.n8n-chat.window-mode{position:fixed;bottom:20px;right:20px;width:var(--n8n-chat-window-width);height:var(--n8n-chat-window-height);border-radius:12px;box-shadow:0 4px 24px #00000026;z-index:9999}.n8n-chat.fullscreen-mode{position:absolute;top:0;left:0;right:0;bottom:0}.n8n-chat-bubble.animate-in{animation:n8n-chat-bubble-in .4s cubic-bezier(.34,1.56,.64,1) forwards}@keyframes n8n-chat-bubble-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@media (max-width: 480px){.n8n-chat.window-mode{bottom:0;right:0;left:0;width:100%;height:100%;border-radius:0}}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import './styles.css';
|
|
2
|
+
import type { ChatOptions, ChatInstance } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new chat instance
|
|
5
|
+
*/
|
|
6
|
+
export declare function createChat(options: ChatOptions): ChatInstance;
|
|
7
|
+
export type { ChatOptions, ChatInstance, ThemeOptions, I18nOptions } from './types';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAgCzD;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAwT7D;AAGD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for n8n-chat-pretty
|
|
3
|
+
*/
|
|
4
|
+
export interface ChatOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The URL of your n8n webhook endpoint
|
|
7
|
+
* @required
|
|
8
|
+
*/
|
|
9
|
+
webhookUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for the webhook request
|
|
12
|
+
*/
|
|
13
|
+
webhookConfig?: {
|
|
14
|
+
method?: 'GET' | 'POST';
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* CSS selector for the target container
|
|
19
|
+
* @default '#n8n-chat'
|
|
20
|
+
*/
|
|
21
|
+
target?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Render mode
|
|
24
|
+
* @default 'fullscreen'
|
|
25
|
+
*/
|
|
26
|
+
mode?: 'window' | 'fullscreen';
|
|
27
|
+
/**
|
|
28
|
+
* Initial greeting messages
|
|
29
|
+
*/
|
|
30
|
+
initialMessages?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* Key used for sending chat input to n8n
|
|
33
|
+
* @default 'chatInput'
|
|
34
|
+
*/
|
|
35
|
+
chatInputKey?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Key used for session ID
|
|
38
|
+
* @default 'sessionId'
|
|
39
|
+
*/
|
|
40
|
+
chatSessionKey?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to load previous session
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
loadPreviousSession?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Additional metadata to send with each message
|
|
48
|
+
*/
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
/**
|
|
51
|
+
* Theme configuration
|
|
52
|
+
*/
|
|
53
|
+
theme?: ThemeOptions;
|
|
54
|
+
/**
|
|
55
|
+
* Internationalization strings
|
|
56
|
+
*/
|
|
57
|
+
i18n?: I18nOptions;
|
|
58
|
+
/**
|
|
59
|
+
* Typing indicator settings
|
|
60
|
+
*/
|
|
61
|
+
typingIndicator?: {
|
|
62
|
+
/**
|
|
63
|
+
* Milliseconds per character for typing delay
|
|
64
|
+
* @default 20
|
|
65
|
+
*/
|
|
66
|
+
msPerChar?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Base delay in milliseconds
|
|
69
|
+
* @default 300
|
|
70
|
+
*/
|
|
71
|
+
baseDelay?: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Maximum characters per message bubble before splitting
|
|
75
|
+
* @default 200
|
|
76
|
+
*/
|
|
77
|
+
maxBubbleLength?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Enable bubble animations
|
|
80
|
+
* @default true
|
|
81
|
+
*/
|
|
82
|
+
enableAnimations?: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface ThemeOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Primary accent color (user messages, buttons)
|
|
87
|
+
* @default '#e74266'
|
|
88
|
+
*/
|
|
89
|
+
primaryColor?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Background color for bot messages
|
|
92
|
+
* @default 'rgba(206,206,206,.5)'
|
|
93
|
+
*/
|
|
94
|
+
botMessageBackground?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Background color for user messages
|
|
97
|
+
* @default '#e74266'
|
|
98
|
+
*/
|
|
99
|
+
userMessageBackground?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Main background color
|
|
102
|
+
* @default '#FFF'
|
|
103
|
+
*/
|
|
104
|
+
backgroundColor?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Main text color
|
|
107
|
+
* @default '#000'
|
|
108
|
+
*/
|
|
109
|
+
textColor?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Font family
|
|
112
|
+
* @default 'system-ui, -apple-system, sans-serif'
|
|
113
|
+
*/
|
|
114
|
+
fontFamily?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Border radius for bubbles
|
|
117
|
+
* @default '1.25rem'
|
|
118
|
+
*/
|
|
119
|
+
borderRadius?: string;
|
|
120
|
+
}
|
|
121
|
+
export interface I18nOptions {
|
|
122
|
+
/**
|
|
123
|
+
* Placeholder text for input field
|
|
124
|
+
* @default 'Type your message...'
|
|
125
|
+
*/
|
|
126
|
+
inputPlaceholder?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Send button text
|
|
129
|
+
* @default 'Send'
|
|
130
|
+
*/
|
|
131
|
+
sendButtonText?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Error message for connection failures
|
|
134
|
+
* @default 'Connection error. Please try again.'
|
|
135
|
+
*/
|
|
136
|
+
errorMessage?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Fallback response when bot returns empty
|
|
139
|
+
* @default "Sorry, I couldn't process that."
|
|
140
|
+
*/
|
|
141
|
+
fallbackResponse?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Chat instance returned by createChat
|
|
145
|
+
*/
|
|
146
|
+
export interface ChatInstance {
|
|
147
|
+
/**
|
|
148
|
+
* Send a message programmatically
|
|
149
|
+
*/
|
|
150
|
+
sendMessage: (text: string) => Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Add a message to the chat (without sending to webhook)
|
|
153
|
+
*/
|
|
154
|
+
addMessage: (text: string, position: 'left' | 'right') => void;
|
|
155
|
+
/**
|
|
156
|
+
* Clear all messages
|
|
157
|
+
*/
|
|
158
|
+
clear: () => void;
|
|
159
|
+
/**
|
|
160
|
+
* Destroy the chat instance
|
|
161
|
+
*/
|
|
162
|
+
destroy: () => void;
|
|
163
|
+
/**
|
|
164
|
+
* Get the current session ID
|
|
165
|
+
*/
|
|
166
|
+
getSessionId: () => string;
|
|
167
|
+
/**
|
|
168
|
+
* Reset the session (creates new session ID)
|
|
169
|
+
*/
|
|
170
|
+
resetSession: () => void;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;IAEF;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;IAE/B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;OAEG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IAEnB;;OAEG;IACH,eAAe,CAAC,EAAE;QAChB;;;WAGG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB;;;WAGG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;OAEG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;IAE/D;;OAEG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,YAAY,EAAE,MAAM,MAAM,CAAC;IAE3B;;OAEG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-chat-pretty",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A beautiful, mobile-friendly chat widget for n8n workflows with conversational bubble animations",
|
|
5
|
+
"main": "dist/chat.umd.js",
|
|
6
|
+
"module": "dist/chat.es.js",
|
|
7
|
+
"types": "dist/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/chat.es.js",
|
|
11
|
+
"require": "./dist/chat.umd.js",
|
|
12
|
+
"types": "./dist/types/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./style.css": "./dist/style.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "vite build && tsc --emitDeclarationOnly --outDir dist/types",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"n8n",
|
|
27
|
+
"chat",
|
|
28
|
+
"chatbot",
|
|
29
|
+
"ai",
|
|
30
|
+
"widget",
|
|
31
|
+
"webhook",
|
|
32
|
+
"conversational",
|
|
33
|
+
"bubble",
|
|
34
|
+
"animation"
|
|
35
|
+
],
|
|
36
|
+
"author": "baufer",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/baufergroup/n8n-chat-pretty.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/baufergroup/n8n-chat-pretty#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/baufergroup/n8n-chat-pretty/issues"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"typescript": "^5.3.0",
|
|
48
|
+
"vite": "^5.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|