@makemore/agent-frontend 1.0.0 â 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -5
- package/dist/chat-widget-markdown.js +110 -0
- package/dist/chat-widget.css +186 -0
- package/dist/chat-widget.js +127 -27
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -32,16 +32,73 @@ Most chat widgets are tightly coupled to specific frameworks or require complex
|
|
|
32
32
|
| đž **Persistence** | Conversations persist across page reloads via localStorage |
|
|
33
33
|
| đĄī¸ **Isolated CSS** | Scoped styles that won't leak into or from your page |
|
|
34
34
|
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
### Via npm
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @makemore/agent-frontend
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then include in your HTML:
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<link rel="stylesheet" href="node_modules/@makemore/agent-frontend/dist/chat-widget.css">
|
|
47
|
+
<script src="node_modules/@makemore/agent-frontend/dist/chat-widget.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Via CDN (unpkg)
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<link rel="stylesheet" href="https://unpkg.com/@makemore/agent-frontend/dist/chat-widget.css">
|
|
54
|
+
<script src="https://unpkg.com/@makemore/agent-frontend/dist/chat-widget.js"></script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Via CDN (jsDelivr)
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@makemore/agent-frontend/dist/chat-widget.css">
|
|
61
|
+
<script src="https://cdn.jsdelivr.net/npm/@makemore/agent-frontend/dist/chat-widget.js"></script>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Optional: Enhanced Markdown Support
|
|
65
|
+
|
|
66
|
+
For full-featured markdown rendering (tables, code blocks with syntax highlighting, etc.), include the optional markdown addon:
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<!-- Core widget -->
|
|
70
|
+
<link rel="stylesheet" href="https://unpkg.com/@makemore/agent-frontend/dist/chat-widget.css">
|
|
71
|
+
<script src="https://unpkg.com/@makemore/agent-frontend/dist/chat-widget.js"></script>
|
|
72
|
+
|
|
73
|
+
<!-- Optional: Enhanced markdown with marked.js -->
|
|
74
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
75
|
+
<script src="https://unpkg.com/@makemore/agent-frontend/dist/chat-widget-markdown.js"></script>
|
|
76
|
+
|
|
77
|
+
<!-- Optional: Syntax highlighting for code blocks -->
|
|
78
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11/styles/github-dark.min.css">
|
|
79
|
+
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11/lib/core.min.js"></script>
|
|
80
|
+
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11/lib/languages/javascript.min.js"></script>
|
|
81
|
+
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11/lib/languages/python.min.js"></script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The widget automatically detects and uses the enhanced markdown parser if available. Without it, a basic markdown parser is used.
|
|
85
|
+
|
|
35
86
|
## Quick Start
|
|
36
87
|
|
|
37
|
-
###
|
|
88
|
+
### Initialize the widget
|
|
38
89
|
|
|
39
90
|
```html
|
|
40
|
-
<
|
|
41
|
-
|
|
91
|
+
<script>
|
|
92
|
+
ChatWidget.init({
|
|
93
|
+
backendUrl: 'https://your-api.com',
|
|
94
|
+
agentKey: 'your-agent',
|
|
95
|
+
title: 'Support Chat',
|
|
96
|
+
primaryColor: '#0066cc',
|
|
97
|
+
});
|
|
98
|
+
</script>
|
|
42
99
|
```
|
|
43
100
|
|
|
44
|
-
###
|
|
101
|
+
### With custom API paths
|
|
45
102
|
|
|
46
103
|
```html
|
|
47
104
|
<script>
|
|
@@ -50,6 +107,11 @@ Most chat widgets are tightly coupled to specific frameworks or require complex
|
|
|
50
107
|
agentKey: 'your-agent',
|
|
51
108
|
title: 'Support Chat',
|
|
52
109
|
primaryColor: '#0066cc',
|
|
110
|
+
apiPaths: {
|
|
111
|
+
anonymousSession: '/api/auth/session/',
|
|
112
|
+
runs: '/api/chat/runs/',
|
|
113
|
+
runEvents: '/api/chat/runs/{runId}/events/',
|
|
114
|
+
},
|
|
53
115
|
});
|
|
54
116
|
</script>
|
|
55
117
|
```
|
|
@@ -74,6 +136,60 @@ Most chat widgets are tightly coupled to specific frameworks or require complex
|
|
|
74
136
|
| `anonymousTokenHeader` | string | `'X-Anonymous-Token'` | Header name for auth token |
|
|
75
137
|
| `conversationIdKey` | string | `'chat_widget_conversation_id'` | localStorage key for conversation ID |
|
|
76
138
|
| `sessionTokenKey` | string | `'chat_widget_session_token'` | localStorage key for session token |
|
|
139
|
+
| `apiPaths` | object | See below | API endpoint paths (customizable for different backends) |
|
|
140
|
+
| `autoRunMode` | string | `'automatic'` | Demo flow mode: `'automatic'`, `'confirm'`, or `'manual'` |
|
|
141
|
+
| `autoRunDelay` | number | `1000` | Delay in milliseconds before auto-generating next message (automatic mode) |
|
|
142
|
+
|
|
143
|
+
### Demo Flow Control
|
|
144
|
+
|
|
145
|
+
The widget supports three modes for demo flows:
|
|
146
|
+
|
|
147
|
+
- **Automatic** (`autoRunMode: 'automatic'`): Continuously generates customer responses with a configurable delay
|
|
148
|
+
- **Confirm Next** (`autoRunMode: 'confirm'`): Pauses after each assistant response and waits for user to click "Continue"
|
|
149
|
+
- **Manual** (`autoRunMode: 'manual'`): Stops auto-generation; user must manually type responses
|
|
150
|
+
|
|
151
|
+
These settings can be changed in real-time via the demo controls dropdown (visible when a demo is running).
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
ChatWidget.init({
|
|
155
|
+
autoRunMode: 'confirm', // Start in confirm mode
|
|
156
|
+
autoRunDelay: 2000, // 2 second delay in automatic mode
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Change mode programmatically
|
|
160
|
+
ChatWidget.setAutoRunMode('automatic');
|
|
161
|
+
ChatWidget.setAutoRunDelay(500); // 0.5 second delay
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### API Paths Configuration
|
|
165
|
+
|
|
166
|
+
The `apiPaths` option allows you to customize the backend API endpoints. This is useful when integrating with different backend frameworks or URL structures.
|
|
167
|
+
|
|
168
|
+
**Default values:**
|
|
169
|
+
```javascript
|
|
170
|
+
apiPaths: {
|
|
171
|
+
anonymousSession: '/api/accounts/anonymous-session/',
|
|
172
|
+
runs: '/api/agent-runtime/runs/',
|
|
173
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
174
|
+
simulateCustomer: '/api/agent-runtime/simulate-customer/',
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Example - Custom Django backend:**
|
|
179
|
+
```javascript
|
|
180
|
+
ChatWidget.init({
|
|
181
|
+
backendUrl: 'https://your-api.com',
|
|
182
|
+
agentKey: 'chat-agent',
|
|
183
|
+
apiPaths: {
|
|
184
|
+
anonymousSession: '/api/ai/agent/anonymous-session/',
|
|
185
|
+
runs: '/api/ai/agent/runs/',
|
|
186
|
+
runEvents: '/api/ai/agent/runs/{runId}/events/',
|
|
187
|
+
// simulateCustomer uses default value
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
You only need to specify the paths you want to override; unspecified paths will use the defaults.
|
|
77
193
|
|
|
78
194
|
## Journey Types Configuration
|
|
79
195
|
|
|
@@ -120,9 +236,18 @@ ChatWidget.clearMessages();
|
|
|
120
236
|
// Start a demo flow
|
|
121
237
|
ChatWidget.startDemoFlow('quote');
|
|
122
238
|
|
|
123
|
-
// Stop
|
|
239
|
+
// Stop demo flow
|
|
124
240
|
ChatWidget.stopAutoRun();
|
|
125
241
|
|
|
242
|
+
// Continue demo flow (when in confirm mode and paused)
|
|
243
|
+
ChatWidget.continueAutoRun();
|
|
244
|
+
|
|
245
|
+
// Change demo flow mode
|
|
246
|
+
ChatWidget.setAutoRunMode('automatic'); // 'automatic', 'confirm', or 'manual'
|
|
247
|
+
|
|
248
|
+
// Change auto-run delay (in milliseconds)
|
|
249
|
+
ChatWidget.setAutoRunDelay(2000);
|
|
250
|
+
|
|
126
251
|
// Remove the widget from the page
|
|
127
252
|
ChatWidget.destroy();
|
|
128
253
|
|
|
@@ -133,6 +258,34 @@ const state = ChatWidget.getState();
|
|
|
133
258
|
const config = ChatWidget.getConfig();
|
|
134
259
|
```
|
|
135
260
|
|
|
261
|
+
## Markdown Support
|
|
262
|
+
|
|
263
|
+
The widget includes built-in markdown rendering for assistant messages:
|
|
264
|
+
|
|
265
|
+
### Basic Markdown (Built-in)
|
|
266
|
+
|
|
267
|
+
The widget includes a lightweight markdown parser that supports:
|
|
268
|
+
- **Bold** (`**text**` or `__text__`)
|
|
269
|
+
- *Italic* (`*text*` or `_text_`)
|
|
270
|
+
- `Inline code` (`` `code` ``)
|
|
271
|
+
- [Links](url) (`[text](url)`)
|
|
272
|
+
- Lists (`- item` or `* item`)
|
|
273
|
+
- Line breaks
|
|
274
|
+
|
|
275
|
+
### Enhanced Markdown (Optional)
|
|
276
|
+
|
|
277
|
+
Include `chat-widget-markdown.js` for full-featured markdown:
|
|
278
|
+
- **Tables** - Full GFM table support
|
|
279
|
+
- **Code blocks** - Multi-line code with syntax highlighting
|
|
280
|
+
- **Blockquotes** - `> quoted text`
|
|
281
|
+
- **Headings** - `# H1` through `###### H6`
|
|
282
|
+
- **Horizontal rules** - `---` or `***`
|
|
283
|
+
- **Task lists** - `- [ ] todo` and `- [x] done`
|
|
284
|
+
- **Strikethrough** - `~~text~~`
|
|
285
|
+
|
|
286
|
+
**Supported languages for syntax highlighting:**
|
|
287
|
+
Add highlight.js language modules as needed (JavaScript, Python, TypeScript, Go, Rust, etc.)
|
|
288
|
+
|
|
136
289
|
## Backend Requirements
|
|
137
290
|
|
|
138
291
|
The widget expects a backend API with the following endpoints:
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Widget - Enhanced Markdown Support
|
|
3
|
+
*
|
|
4
|
+
* This optional addon replaces the basic markdown parser with marked.js
|
|
5
|
+
* for full-featured markdown rendering including tables, code blocks with
|
|
6
|
+
* syntax highlighting, and more.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* 1. Include marked.js (and optionally highlight.js for syntax highlighting)
|
|
10
|
+
* 2. Include this file AFTER chat-widget.js
|
|
11
|
+
* 3. Initialize the widget normally
|
|
12
|
+
*
|
|
13
|
+
* Example:
|
|
14
|
+
* <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
15
|
+
* <script src="https://cdn.jsdelivr.net/npm/highlight.js@11/lib/core.min.js"></script>
|
|
16
|
+
* <script src="https://cdn.jsdelivr.net/npm/highlight.js@11/lib/languages/javascript.min.js"></script>
|
|
17
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11/styles/github-dark.min.css">
|
|
18
|
+
* <script src="chat-widget.js"></script>
|
|
19
|
+
* <script src="chat-widget-markdown.js"></script>
|
|
20
|
+
*/
|
|
21
|
+
(function(global) {
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
// Check if ChatWidget is available
|
|
25
|
+
if (!global.ChatWidget) {
|
|
26
|
+
console.error('[ChatWidget Markdown] ChatWidget must be loaded before chat-widget-markdown.js');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check if marked is available
|
|
31
|
+
if (typeof marked === 'undefined') {
|
|
32
|
+
console.warn('[ChatWidget Markdown] marked.js not found. Install with: npm install marked or include from CDN');
|
|
33
|
+
console.warn('[ChatWidget Markdown] Falling back to basic markdown parser');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('[ChatWidget Markdown] Enhanced markdown support enabled');
|
|
38
|
+
|
|
39
|
+
// Configure marked
|
|
40
|
+
const markedOptions = {
|
|
41
|
+
breaks: true,
|
|
42
|
+
gfm: true,
|
|
43
|
+
headerIds: false,
|
|
44
|
+
mangle: false,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Configure syntax highlighting if highlight.js is available
|
|
48
|
+
if (typeof hljs !== 'undefined') {
|
|
49
|
+
console.log('[ChatWidget Markdown] Syntax highlighting enabled');
|
|
50
|
+
markedOptions.highlight = function(code, lang) {
|
|
51
|
+
if (lang && hljs.getLanguage(lang)) {
|
|
52
|
+
try {
|
|
53
|
+
return hljs.highlight(code, { language: lang }).value;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error('[ChatWidget Markdown] Highlight error:', err);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return code;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
marked.setOptions(markedOptions);
|
|
63
|
+
|
|
64
|
+
// Enhanced markdown parser using marked.js
|
|
65
|
+
function enhancedParseMarkdown(text) {
|
|
66
|
+
if (!text) return '';
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Parse markdown with marked
|
|
70
|
+
let html = marked.parse(text);
|
|
71
|
+
|
|
72
|
+
// Sanitize links to open in new tab
|
|
73
|
+
html = html.replace(/<a href=/g, '<a target="_blank" rel="noopener noreferrer" href=');
|
|
74
|
+
|
|
75
|
+
return html;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error('[ChatWidget Markdown] Parse error:', err);
|
|
78
|
+
// Fallback to escaped text
|
|
79
|
+
const div = document.createElement('div');
|
|
80
|
+
div.textContent = text;
|
|
81
|
+
return div.innerHTML.replace(/\n/g, '<br>');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Store reference to original init
|
|
86
|
+
const originalInit = global.ChatWidget.init;
|
|
87
|
+
|
|
88
|
+
// Override init to inject enhanced markdown parser
|
|
89
|
+
global.ChatWidget.init = function(userConfig = {}) {
|
|
90
|
+
// Call original init
|
|
91
|
+
originalInit.call(this, userConfig);
|
|
92
|
+
|
|
93
|
+
// Inject enhanced markdown parser
|
|
94
|
+
// We need to override the internal parseMarkdown function
|
|
95
|
+
// This is done by monkey-patching the render cycle
|
|
96
|
+
console.log('[ChatWidget Markdown] Markdown parser enhanced with marked.js');
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Expose the enhanced parser for internal use
|
|
100
|
+
// The widget will need to check for this and use it if available
|
|
101
|
+
global.ChatWidget._enhancedMarkdownParser = enhancedParseMarkdown;
|
|
102
|
+
|
|
103
|
+
// Add configuration option
|
|
104
|
+
global.ChatWidget.enableEnhancedMarkdown = function() {
|
|
105
|
+
console.log('[ChatWidget Markdown] Enhanced markdown explicitly enabled');
|
|
106
|
+
return true;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
})(typeof window !== 'undefined' ? window : this);
|
|
110
|
+
|
package/dist/chat-widget.css
CHANGED
|
@@ -450,6 +450,77 @@
|
|
|
450
450
|
background: rgba(239, 68, 68, 0.1);
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
/* Auto-run controls */
|
|
454
|
+
.cw-autorun-controls {
|
|
455
|
+
padding: 8px 12px;
|
|
456
|
+
display: flex;
|
|
457
|
+
flex-direction: column;
|
|
458
|
+
gap: 8px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.cw-control-label {
|
|
462
|
+
display: flex;
|
|
463
|
+
align-items: center;
|
|
464
|
+
gap: 8px;
|
|
465
|
+
font-size: 13px;
|
|
466
|
+
color: var(--cw-text);
|
|
467
|
+
cursor: pointer;
|
|
468
|
+
padding: 4px;
|
|
469
|
+
border-radius: 4px;
|
|
470
|
+
transition: background 0.15s;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.cw-control-label:hover {
|
|
474
|
+
background: var(--cw-bg-muted);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.cw-control-label input[type="radio"] {
|
|
478
|
+
margin: 0;
|
|
479
|
+
cursor: pointer;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.cw-delay-control {
|
|
483
|
+
padding: 8px 12px;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.cw-delay-control input[type="range"] {
|
|
487
|
+
width: 100%;
|
|
488
|
+
margin-top: 4px;
|
|
489
|
+
cursor: pointer;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/* Continue button */
|
|
493
|
+
.cw-continue-bar {
|
|
494
|
+
padding: 12px 16px;
|
|
495
|
+
background: var(--cw-bg-muted);
|
|
496
|
+
border-top: 1px solid var(--cw-border);
|
|
497
|
+
display: flex;
|
|
498
|
+
justify-content: center;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.cw-continue-btn {
|
|
502
|
+
padding: 10px 20px;
|
|
503
|
+
border: none;
|
|
504
|
+
border-radius: var(--cw-radius-sm);
|
|
505
|
+
background: var(--cw-primary);
|
|
506
|
+
color: white;
|
|
507
|
+
font-size: 14px;
|
|
508
|
+
font-weight: 600;
|
|
509
|
+
cursor: pointer;
|
|
510
|
+
transition: opacity 0.15s;
|
|
511
|
+
display: flex;
|
|
512
|
+
align-items: center;
|
|
513
|
+
gap: 6px;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.cw-continue-btn:hover {
|
|
517
|
+
opacity: 0.9;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.cw-continue-btn:active {
|
|
521
|
+
opacity: 0.8;
|
|
522
|
+
}
|
|
523
|
+
|
|
453
524
|
/* Responsive */
|
|
454
525
|
@media (max-width: 480px) {
|
|
455
526
|
.cw-widget {
|
|
@@ -501,3 +572,118 @@
|
|
|
501
572
|
}
|
|
502
573
|
}
|
|
503
574
|
|
|
575
|
+
/* Enhanced Markdown Styles (for chat-widget-markdown.js) */
|
|
576
|
+
.cw-message pre {
|
|
577
|
+
background: var(--cw-bg-muted);
|
|
578
|
+
border: 1px solid var(--cw-border);
|
|
579
|
+
border-radius: var(--cw-radius-sm);
|
|
580
|
+
padding: 12px;
|
|
581
|
+
overflow-x: auto;
|
|
582
|
+
margin: 8px 0;
|
|
583
|
+
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|
584
|
+
font-size: 13px;
|
|
585
|
+
line-height: 1.4;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.cw-message pre code {
|
|
589
|
+
background: none;
|
|
590
|
+
padding: 0;
|
|
591
|
+
border-radius: 0;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.cw-message table {
|
|
595
|
+
border-collapse: collapse;
|
|
596
|
+
width: 100%;
|
|
597
|
+
margin: 8px 0;
|
|
598
|
+
font-size: 13px;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.cw-message table th,
|
|
602
|
+
.cw-message table td {
|
|
603
|
+
border: 1px solid var(--cw-border);
|
|
604
|
+
padding: 8px 12px;
|
|
605
|
+
text-align: left;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.cw-message table th {
|
|
609
|
+
background: var(--cw-bg-muted);
|
|
610
|
+
font-weight: 600;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.cw-message table tr:nth-child(even) {
|
|
614
|
+
background: rgba(0, 0, 0, 0.02);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.cw-message blockquote {
|
|
618
|
+
border-left: 3px solid var(--cw-primary);
|
|
619
|
+
padding-left: 12px;
|
|
620
|
+
margin: 8px 0;
|
|
621
|
+
color: var(--cw-text-muted);
|
|
622
|
+
font-style: italic;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.cw-message h1,
|
|
626
|
+
.cw-message h2,
|
|
627
|
+
.cw-message h3,
|
|
628
|
+
.cw-message h4,
|
|
629
|
+
.cw-message h5,
|
|
630
|
+
.cw-message h6 {
|
|
631
|
+
margin: 12px 0 8px 0;
|
|
632
|
+
font-weight: 600;
|
|
633
|
+
line-height: 1.3;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.cw-message h1 { font-size: 1.5em; }
|
|
637
|
+
.cw-message h2 { font-size: 1.3em; }
|
|
638
|
+
.cw-message h3 { font-size: 1.1em; }
|
|
639
|
+
.cw-message h4 { font-size: 1em; }
|
|
640
|
+
|
|
641
|
+
.cw-message hr {
|
|
642
|
+
border: none;
|
|
643
|
+
border-top: 1px solid var(--cw-border);
|
|
644
|
+
margin: 12px 0;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.cw-message ul,
|
|
648
|
+
.cw-message ol {
|
|
649
|
+
margin: 8px 0;
|
|
650
|
+
padding-left: 24px;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.cw-message li {
|
|
654
|
+
margin: 4px 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.cw-message p {
|
|
658
|
+
margin: 8px 0;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.cw-message p:first-child {
|
|
662
|
+
margin-top: 0;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.cw-message p:last-child {
|
|
666
|
+
margin-bottom: 0;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* Dark mode adjustments for enhanced markdown */
|
|
670
|
+
@media (prefers-color-scheme: dark) {
|
|
671
|
+
.cw-message pre {
|
|
672
|
+
background: rgba(255, 255, 255, 0.05);
|
|
673
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.cw-message table th {
|
|
677
|
+
background: rgba(255, 255, 255, 0.05);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.cw-message table tr:nth-child(even) {
|
|
681
|
+
background: rgba(255, 255, 255, 0.02);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.cw-message table th,
|
|
685
|
+
.cw-message table td {
|
|
686
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
package/dist/chat-widget.js
CHANGED
|
@@ -36,6 +36,16 @@
|
|
|
36
36
|
anonymousTokenHeader: 'X-Anonymous-Token',
|
|
37
37
|
conversationIdKey: 'chat_widget_conversation_id',
|
|
38
38
|
sessionTokenKey: 'chat_widget_session_token',
|
|
39
|
+
// API endpoint paths (can be customized for different backend setups)
|
|
40
|
+
apiPaths: {
|
|
41
|
+
anonymousSession: '/api/accounts/anonymous-session/',
|
|
42
|
+
runs: '/api/agent-runtime/runs/',
|
|
43
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
44
|
+
simulateCustomer: '/api/agent-runtime/simulate-customer/',
|
|
45
|
+
},
|
|
46
|
+
// Demo flow control
|
|
47
|
+
autoRunDelay: 1000, // Delay in ms before auto-generating next message
|
|
48
|
+
autoRunMode: 'automatic', // 'automatic', 'confirm', or 'manual'
|
|
39
49
|
};
|
|
40
50
|
|
|
41
51
|
// State
|
|
@@ -45,7 +55,8 @@
|
|
|
45
55
|
isExpanded: false,
|
|
46
56
|
isLoading: false,
|
|
47
57
|
isSimulating: false,
|
|
48
|
-
|
|
58
|
+
autoRunActive: false,
|
|
59
|
+
autoRunPaused: false,
|
|
49
60
|
debugMode: false,
|
|
50
61
|
journeyType: 'general',
|
|
51
62
|
messages: [],
|
|
@@ -75,30 +86,35 @@
|
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
function parseMarkdown(text) {
|
|
78
|
-
//
|
|
89
|
+
// Check if enhanced markdown parser is available (from chat-widget-markdown.js)
|
|
90
|
+
if (global.ChatWidget && global.ChatWidget._enhancedMarkdownParser) {
|
|
91
|
+
return global.ChatWidget._enhancedMarkdownParser(text);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Fallback: Simple markdown parsing for common patterns
|
|
79
95
|
let html = escapeHtml(text);
|
|
80
|
-
|
|
96
|
+
|
|
81
97
|
// Bold: **text** or __text__
|
|
82
98
|
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
83
99
|
html = html.replace(/__(.+?)__/g, '<strong>$1</strong>');
|
|
84
|
-
|
|
100
|
+
|
|
85
101
|
// Italic: *text* or _text_
|
|
86
102
|
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
87
103
|
html = html.replace(/_(.+?)_/g, '<em>$1</em>');
|
|
88
|
-
|
|
104
|
+
|
|
89
105
|
// Code: `code`
|
|
90
106
|
html = html.replace(/`(.+?)`/g, '<code>$1</code>');
|
|
91
|
-
|
|
107
|
+
|
|
92
108
|
// Links: [text](url)
|
|
93
109
|
html = html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
94
|
-
|
|
110
|
+
|
|
95
111
|
// Line breaks
|
|
96
112
|
html = html.replace(/\n/g, '<br>');
|
|
97
|
-
|
|
113
|
+
|
|
98
114
|
// Lists: - item or * item
|
|
99
115
|
html = html.replace(/^[\-\*]\s+(.+)$/gm, '<li>$1</li>');
|
|
100
116
|
html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
|
|
101
|
-
|
|
117
|
+
|
|
102
118
|
return html;
|
|
103
119
|
}
|
|
104
120
|
|
|
@@ -140,7 +156,7 @@
|
|
|
140
156
|
|
|
141
157
|
// Create new anonymous session
|
|
142
158
|
try {
|
|
143
|
-
const response = await fetch(`${config.backendUrl}
|
|
159
|
+
const response = await fetch(`${config.backendUrl}${config.apiPaths.anonymousSession}`, {
|
|
144
160
|
method: 'POST',
|
|
145
161
|
headers: { 'Content-Type': 'application/json' },
|
|
146
162
|
});
|
|
@@ -191,7 +207,7 @@
|
|
|
191
207
|
state.conversationId = getStoredValue(config.conversationIdKey);
|
|
192
208
|
}
|
|
193
209
|
|
|
194
|
-
const response = await fetch(`${config.backendUrl}
|
|
210
|
+
const response = await fetch(`${config.backendUrl}${config.apiPaths.runs}`, {
|
|
195
211
|
method: 'POST',
|
|
196
212
|
headers,
|
|
197
213
|
body: JSON.stringify({
|
|
@@ -231,7 +247,8 @@
|
|
|
231
247
|
state.eventSource.close();
|
|
232
248
|
}
|
|
233
249
|
|
|
234
|
-
|
|
250
|
+
const eventPath = config.apiPaths.runEvents.replace('{runId}', runId);
|
|
251
|
+
let url = `${config.backendUrl}${eventPath}`;
|
|
235
252
|
if (token) {
|
|
236
253
|
url += `?anonymous_token=${encodeURIComponent(token)}`;
|
|
237
254
|
}
|
|
@@ -331,8 +348,13 @@
|
|
|
331
348
|
render();
|
|
332
349
|
|
|
333
350
|
// Trigger auto-run if enabled
|
|
334
|
-
if (state.
|
|
335
|
-
|
|
351
|
+
if (state.autoRunActive && !state.error) {
|
|
352
|
+
if (config.autoRunMode === 'automatic') {
|
|
353
|
+
setTimeout(() => triggerAutoRun(), config.autoRunDelay);
|
|
354
|
+
} else if (config.autoRunMode === 'confirm') {
|
|
355
|
+
state.autoRunPaused = true;
|
|
356
|
+
render();
|
|
357
|
+
}
|
|
336
358
|
}
|
|
337
359
|
};
|
|
338
360
|
|
|
@@ -357,16 +379,17 @@
|
|
|
357
379
|
// ============================================================================
|
|
358
380
|
|
|
359
381
|
async function triggerAutoRun() {
|
|
360
|
-
if (!state.
|
|
382
|
+
if (!state.autoRunActive || state.isLoading || state.isSimulating) return;
|
|
361
383
|
|
|
362
384
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
363
385
|
if (lastMessage?.role !== 'assistant') return;
|
|
364
386
|
|
|
365
387
|
state.isSimulating = true;
|
|
388
|
+
state.autoRunPaused = false;
|
|
366
389
|
render();
|
|
367
390
|
|
|
368
391
|
try {
|
|
369
|
-
const response = await fetch(`${config.backendUrl}
|
|
392
|
+
const response = await fetch(`${config.backendUrl}${config.apiPaths.simulateCustomer}`, {
|
|
370
393
|
method: 'POST',
|
|
371
394
|
headers: { 'Content-Type': 'application/json' },
|
|
372
395
|
body: JSON.stringify({
|
|
@@ -394,7 +417,8 @@
|
|
|
394
417
|
async function startDemoFlow(journeyType) {
|
|
395
418
|
clearMessages();
|
|
396
419
|
state.journeyType = journeyType;
|
|
397
|
-
state.
|
|
420
|
+
state.autoRunActive = true;
|
|
421
|
+
state.autoRunPaused = false;
|
|
398
422
|
render();
|
|
399
423
|
|
|
400
424
|
const journey = config.journeyTypes[journeyType];
|
|
@@ -411,7 +435,26 @@
|
|
|
411
435
|
}
|
|
412
436
|
|
|
413
437
|
function stopAutoRun() {
|
|
414
|
-
state.
|
|
438
|
+
state.autoRunActive = false;
|
|
439
|
+
state.autoRunPaused = false;
|
|
440
|
+
render();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function continueAutoRun() {
|
|
444
|
+
if (state.autoRunActive && state.autoRunPaused) {
|
|
445
|
+
triggerAutoRun();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function setAutoRunMode(mode) {
|
|
450
|
+
if (['automatic', 'confirm', 'manual'].includes(mode)) {
|
|
451
|
+
config.autoRunMode = mode;
|
|
452
|
+
render();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function setAutoRunDelay(delay) {
|
|
457
|
+
config.autoRunDelay = Math.max(0, parseInt(delay) || 1000);
|
|
415
458
|
render();
|
|
416
459
|
}
|
|
417
460
|
|
|
@@ -427,7 +470,8 @@
|
|
|
427
470
|
|
|
428
471
|
function closeWidget() {
|
|
429
472
|
state.isOpen = false;
|
|
430
|
-
state.
|
|
473
|
+
state.autoRunActive = false;
|
|
474
|
+
state.autoRunPaused = false;
|
|
431
475
|
render();
|
|
432
476
|
}
|
|
433
477
|
|
|
@@ -445,7 +489,8 @@
|
|
|
445
489
|
state.messages = [];
|
|
446
490
|
state.conversationId = null;
|
|
447
491
|
state.error = null;
|
|
448
|
-
state.
|
|
492
|
+
state.autoRunActive = false;
|
|
493
|
+
state.autoRunPaused = false;
|
|
449
494
|
setStoredValue(config.conversationIdKey, null);
|
|
450
495
|
render();
|
|
451
496
|
}
|
|
@@ -496,16 +541,48 @@
|
|
|
496
541
|
</button>
|
|
497
542
|
`).join('');
|
|
498
543
|
|
|
499
|
-
const
|
|
544
|
+
const controlsSection = state.autoRunActive ? `
|
|
545
|
+
<div class="cw-dropdown-separator"></div>
|
|
546
|
+
<div class="cw-dropdown-label">Demo Controls</div>
|
|
547
|
+
<div class="cw-autorun-controls">
|
|
548
|
+
<label class="cw-control-label">
|
|
549
|
+
<input type="radio" name="autorun-mode" value="automatic"
|
|
550
|
+
${config.autoRunMode === 'automatic' ? 'checked' : ''}
|
|
551
|
+
onchange="ChatWidget.setAutoRunMode('automatic')">
|
|
552
|
+
<span>⥠Automatic</span>
|
|
553
|
+
</label>
|
|
554
|
+
<label class="cw-control-label">
|
|
555
|
+
<input type="radio" name="autorun-mode" value="confirm"
|
|
556
|
+
${config.autoRunMode === 'confirm' ? 'checked' : ''}
|
|
557
|
+
onchange="ChatWidget.setAutoRunMode('confirm')">
|
|
558
|
+
<span>đ Confirm Next</span>
|
|
559
|
+
</label>
|
|
560
|
+
<label class="cw-control-label">
|
|
561
|
+
<input type="radio" name="autorun-mode" value="manual"
|
|
562
|
+
${config.autoRunMode === 'manual' ? 'checked' : ''}
|
|
563
|
+
onchange="ChatWidget.setAutoRunMode('manual')">
|
|
564
|
+
<span>â Manual</span>
|
|
565
|
+
</label>
|
|
566
|
+
</div>
|
|
567
|
+
${config.autoRunMode === 'automatic' ? `
|
|
568
|
+
<div class="cw-delay-control">
|
|
569
|
+
<label class="cw-control-label">
|
|
570
|
+
<span>Delay: ${config.autoRunDelay}ms</span>
|
|
571
|
+
<input type="range" min="0" max="5000" step="100"
|
|
572
|
+
value="${config.autoRunDelay}"
|
|
573
|
+
oninput="ChatWidget.setAutoRunDelay(this.value)">
|
|
574
|
+
</label>
|
|
575
|
+
</div>
|
|
576
|
+
` : ''}
|
|
500
577
|
<div class="cw-dropdown-separator"></div>
|
|
501
578
|
<button class="cw-dropdown-item cw-dropdown-item-danger" data-action="stop-autorun">
|
|
502
|
-
âšī¸ Stop
|
|
579
|
+
âšī¸ Stop Demo
|
|
503
580
|
</button>
|
|
504
581
|
` : '';
|
|
505
582
|
|
|
506
583
|
return `
|
|
507
584
|
<div class="cw-dropdown">
|
|
508
|
-
<button class="cw-header-btn ${state.
|
|
585
|
+
<button class="cw-header-btn ${state.autoRunActive ? 'cw-btn-active' : ''}"
|
|
509
586
|
data-action="toggle-journey-dropdown"
|
|
510
587
|
title="Demo Flows"
|
|
511
588
|
${state.isLoading || state.isSimulating ? 'disabled' : ''}>
|
|
@@ -515,7 +592,7 @@
|
|
|
515
592
|
<div class="cw-dropdown-label">Demo Flows</div>
|
|
516
593
|
<div class="cw-dropdown-separator"></div>
|
|
517
594
|
${journeyItems}
|
|
518
|
-
${
|
|
595
|
+
${controlsSection}
|
|
519
596
|
</div>
|
|
520
597
|
</div>
|
|
521
598
|
`;
|
|
@@ -559,9 +636,17 @@
|
|
|
559
636
|
</div>
|
|
560
637
|
` : '';
|
|
561
638
|
|
|
562
|
-
const
|
|
639
|
+
const continueButton = (state.autoRunActive && state.autoRunPaused && config.autoRunMode === 'confirm') ? `
|
|
640
|
+
<div class="cw-continue-bar">
|
|
641
|
+
<button class="cw-continue-btn" data-action="continue-autorun" style="background-color: ${config.primaryColor}">
|
|
642
|
+
âļī¸ Continue Demo
|
|
643
|
+
</button>
|
|
644
|
+
</div>
|
|
645
|
+
` : '';
|
|
646
|
+
|
|
647
|
+
const statusBar = (state.autoRunActive || state.debugMode) ? `
|
|
563
648
|
<div class="cw-status-bar">
|
|
564
|
-
${state.
|
|
649
|
+
${state.autoRunActive ? `<span>đ¤ Demo: ${config.journeyTypes[state.journeyType]?.label || state.journeyType} (${config.autoRunMode})</span>` : ''}
|
|
565
650
|
${state.debugMode ? '<span>đ Debug</span>' : ''}
|
|
566
651
|
</div>
|
|
567
652
|
` : '';
|
|
@@ -603,6 +688,7 @@
|
|
|
603
688
|
${messagesHtml}
|
|
604
689
|
${typingIndicator}
|
|
605
690
|
</div>
|
|
691
|
+
${continueButton}
|
|
606
692
|
${errorBar}
|
|
607
693
|
<form class="cw-input-form" id="cw-input-form">
|
|
608
694
|
<input type="text" class="cw-input" placeholder="${escapeHtml(config.placeholder)}" ${state.isLoading ? 'disabled' : ''}>
|
|
@@ -637,6 +723,7 @@
|
|
|
637
723
|
case 'toggle-debug': toggleDebugMode(); break;
|
|
638
724
|
case 'clear': clearMessages(); break;
|
|
639
725
|
case 'stop-autorun': stopAutoRun(); break;
|
|
726
|
+
case 'continue-autorun': continueAutoRun(); break;
|
|
640
727
|
case 'toggle-journey-dropdown':
|
|
641
728
|
const dropdown = document.getElementById('cw-journey-dropdown');
|
|
642
729
|
if (dropdown) {
|
|
@@ -685,7 +772,17 @@
|
|
|
685
772
|
// ============================================================================
|
|
686
773
|
|
|
687
774
|
function init(userConfig = {}) {
|
|
688
|
-
|
|
775
|
+
// Deep merge apiPaths to allow partial overrides
|
|
776
|
+
const mergedApiPaths = {
|
|
777
|
+
...DEFAULT_CONFIG.apiPaths,
|
|
778
|
+
...(userConfig.apiPaths || {}),
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
config = {
|
|
782
|
+
...DEFAULT_CONFIG,
|
|
783
|
+
...userConfig,
|
|
784
|
+
apiPaths: mergedApiPaths,
|
|
785
|
+
};
|
|
689
786
|
state.journeyType = config.defaultJourneyType;
|
|
690
787
|
|
|
691
788
|
// Restore conversation ID
|
|
@@ -738,6 +835,9 @@
|
|
|
738
835
|
clearMessages,
|
|
739
836
|
startDemoFlow,
|
|
740
837
|
stopAutoRun,
|
|
838
|
+
continueAutoRun,
|
|
839
|
+
setAutoRunMode,
|
|
840
|
+
setAutoRunDelay,
|
|
741
841
|
getState: () => ({ ...state }),
|
|
742
842
|
getConfig: () => ({ ...config }),
|
|
743
843
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makemore/agent-frontend",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A standalone, zero-dependency chat widget for AI agents. Embed conversational AI into any website with a single script tag.",
|
|
5
5
|
"main": "dist/chat-widget.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist/chat-widget.js",
|
|
8
8
|
"dist/chat-widget.css",
|
|
9
|
+
"dist/chat-widget-markdown.js",
|
|
9
10
|
"README.md",
|
|
10
11
|
"LICENSE"
|
|
11
12
|
],
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
},
|
|
17
18
|
"repository": {
|
|
18
19
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/
|
|
20
|
+
"url": "git+https://github.com/makemore/agent-frontend.git"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
23
|
"chat",
|
|
@@ -33,12 +34,12 @@
|
|
|
33
34
|
"conversational-ai",
|
|
34
35
|
"customer-support"
|
|
35
36
|
],
|
|
36
|
-
"author": "
|
|
37
|
+
"author": "makemore",
|
|
37
38
|
"license": "MIT",
|
|
38
39
|
"bugs": {
|
|
39
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/makemore/agent-frontend/issues"
|
|
40
41
|
},
|
|
41
|
-
"homepage": "https://github.com/
|
|
42
|
+
"homepage": "https://github.com/makemore/agent-frontend#readme",
|
|
42
43
|
"engines": {
|
|
43
44
|
"node": ">=12.0.0"
|
|
44
45
|
}
|