cmcts-c-agent-embedding 1.0.27 → 1.0.29-cagent
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/.eslintrc.cjs +1 -0
- package/.idea/{Flowise-share-chatbox.iml → flowise-chatbox.iml} +0 -1
- package/.idea/modules.xml +1 -2
- package/.idea/vcs.xml +5 -6
- package/AGENTS.md +166 -0
- package/README.md +349 -349
- package/dist/BubbleChat.d.ts +9 -12
- package/dist/BubbleChat.d.ts.map +1 -1
- package/dist/FullPageChat.d.ts +17 -17
- package/dist/components/Bot.d.ts +11 -0
- package/dist/components/Bot.d.ts.map +1 -1
- package/dist/components/FeedbackContentDialog.d.ts.map +1 -1
- package/dist/components/Toast.d.ts +10 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/bubbles/BotBubble.d.ts +7 -0
- package/dist/components/bubbles/BotBubble.d.ts.map +1 -1
- package/dist/components/bubbles/ChartBubble.d.ts +9 -0
- package/dist/components/bubbles/ChartBubble.d.ts.map +1 -0
- package/dist/components/bubbles/ChartLoadingPlaceholder.d.ts +8 -0
- package/dist/components/bubbles/ChartLoadingPlaceholder.d.ts.map +1 -0
- package/dist/components/bubbles/ChartPlaceholder.d.ts +16 -0
- package/dist/components/bubbles/ChartPlaceholder.d.ts.map +1 -0
- package/dist/components/bubbles/ChartPortalContainer.d.ts +12 -0
- package/dist/components/bubbles/ChartPortalContainer.d.ts.map +1 -0
- package/dist/components/bubbles/LoadingBubble.d.ts +7 -1
- package/dist/components/bubbles/LoadingBubble.d.ts.map +1 -1
- package/dist/components/bubbles/SourceBubble.d.ts.map +1 -1
- package/dist/components/bubbles/StableChartWrapper.d.ts +16 -0
- package/dist/components/bubbles/StableChartWrapper.d.ts.map +1 -0
- package/dist/components/bubbles/StarterPromptBubble.d.ts.map +1 -1
- package/dist/components/buttons/CopyMessageButton.d.ts +10 -0
- package/dist/components/buttons/CopyMessageButton.d.ts.map +1 -0
- package/dist/components/buttons/EditMessageButton.d.ts +8 -0
- package/dist/components/buttons/EditMessageButton.d.ts.map +1 -0
- package/dist/components/buttons/PopupToast.d.ts +7 -0
- package/dist/components/buttons/PopupToast.d.ts.map +1 -0
- package/dist/components/buttons/SendButton.d.ts +1 -0
- package/dist/components/buttons/SendButton.d.ts.map +1 -1
- package/dist/components/dialogs/EditMessageDialog.d.ts +9 -0
- package/dist/components/dialogs/EditMessageDialog.d.ts.map +1 -0
- package/dist/components/icons/AddImageIcon.d.ts.map +1 -1
- package/dist/components/icons/CheckIcon.d.ts +3 -0
- package/dist/components/icons/CheckIcon.d.ts.map +1 -0
- package/dist/components/icons/CopyIcon.d.ts +5 -0
- package/dist/components/icons/CopyIcon.d.ts.map +1 -0
- package/dist/components/icons/EditIcon.d.ts +5 -0
- package/dist/components/icons/EditIcon.d.ts.map +1 -0
- package/dist/components/icons/FullScreenIcon.d.ts +3 -0
- package/dist/components/icons/FullScreenIcon.d.ts.map +1 -0
- package/dist/components/icons/index.d.ts +4 -0
- package/dist/components/icons/index.d.ts.map +1 -1
- package/dist/components/image/PreviewImage.d.ts +5 -0
- package/dist/components/image/PreviewImage.d.ts.map +1 -0
- package/dist/components/inputs/textInput/components/ShortTextInput.d.ts +4 -0
- package/dist/components/inputs/textInput/components/ShortTextInput.d.ts.map +1 -1
- package/dist/components/inputs/textInput/components/TextInput.d.ts +16 -1
- package/dist/components/inputs/textInput/components/TextInput.d.ts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/features/bubble/components/Bubble.d.ts.map +1 -1
- package/dist/features/bubble/types.d.ts +1 -0
- package/dist/features/bubble/types.d.ts.map +1 -1
- package/dist/features/full/components/Full.d.ts.map +1 -1
- package/dist/features/popup/components/Popup.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/queries/sendMessageQuery.d.ts +26 -0
- package/dist/queries/sendMessageQuery.d.ts.map +1 -1
- package/dist/types/chart.d.ts +72 -0
- package/dist/types/chart.d.ts.map +1 -0
- package/dist/utils/chartInstanceManager.d.ts +43 -0
- package/dist/utils/chartInstanceManager.d.ts.map +1 -0
- package/dist/utils/chartPortalManager.d.ts +76 -0
- package/dist/utils/chartPortalManager.d.ts.map +1 -0
- package/dist/utils/chartTagParser.d.ts +39 -0
- package/dist/utils/chartTagParser.d.ts.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/textStreamSmoother.d.ts +40 -0
- package/dist/utils/textStreamSmoother.d.ts.map +1 -0
- package/dist/utils/transcriptApi.d.ts +8 -0
- package/dist/utils/transcriptApi.d.ts.map +1 -0
- package/dist/utils/ultimateJsonParser.d.ts +2 -0
- package/dist/utils/ultimateJsonParser.d.ts.map +1 -0
- package/dist/web.d.ts +4 -0
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +1 -1
- package/dist/window.d.ts +2 -0
- package/dist/window.d.ts.map +1 -1
- package/docs/chart-rendering-implementation-plan.md +565 -0
- package/package.json +9 -5
- package/public/index.html +6 -115
- package/server.js +401 -401
- package/.env +0 -36
- package/.husky/pre-commit +0 -18
- package/.idea/codeStyles/Project.xml +0 -60
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -16
- package/.idea/prettier.xml +0 -6
- package/a.json +0 -57
- package/bun.lockb +0 -0
- package/dist/components/ImageUploadButton.d.ts +0 -11
- package/dist/components/ImageUploadButton.d.ts.map +0 -1
- package/dist/components/RecordAudioButton.d.ts +0 -11
- package/dist/components/RecordAudioButton.d.ts.map +0 -1
- package/dist/components/SendButton.d.ts +0 -12
- package/dist/components/SendButton.d.ts.map +0 -1
- package/test.html +0 -17
package/dist/window.d.ts
CHANGED
package/dist/window.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AAIF,eAAO,MAAM,QAAQ,UAAW,QAAQ,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,SAMzD,CAAC;AAEF,eAAO,MAAM,IAAI,UAAW,QAAQ,SAOnC,CAAC;AAEF,eAAO,MAAM,OAAO,YAEnB,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,OAAO,CAAC;CACzB,CAAC;AAQF,eAAO,MAAM,YAAY;sBAjCO,QAAQ,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE;kBAQ9B,QAAQ;;CA6BlC,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAAS,OAAO,SAGjD,CAAC"}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
# Chart Rendering Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document outlines the implementation plan for rendering Chart.js charts by parsing XML tags from SSE streaming API responses in the Flowise chatbot embed.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Architecture Overview](#architecture-overview)
|
|
10
|
+
2. [Data Flow](#data-flow)
|
|
11
|
+
3. [New Components and Utilities](#new-components-and-utilities)
|
|
12
|
+
4. [Modifications to Existing Components](#modifications-to-existing-components)
|
|
13
|
+
5. [Implementation Steps](#implementation-steps)
|
|
14
|
+
6. [Edge Cases and Error Handling](#edge-cases-and-error-handling)
|
|
15
|
+
7. [Testing Strategy](#testing-strategy)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Architecture Overview
|
|
20
|
+
|
|
21
|
+
### Current Architecture
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
SSE Stream → fetchEventSource → token event → addToStreamBuffer → processTextBuffer → updateLastMessage → BotBubble (Marked.parse) → innerHTML
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Proposed Architecture
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
SSE Stream → fetchEventSource → token event → ChartTagParser (intercepts) →
|
|
31
|
+
├── Regular text → addToStreamBuffer → processTextBuffer → updateLastMessage
|
|
32
|
+
└── Chart tags → ChartBuffer (accumulates) → Complete chart → ChartRenderer
|
|
33
|
+
↓
|
|
34
|
+
BotBubble (renders mixed content: text + charts)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Architectural Decisions
|
|
38
|
+
|
|
39
|
+
1. **Separation of Concerns**: Chart parsing logic is isolated in a dedicated utility (`ChartTagParser`)
|
|
40
|
+
2. **State Machine Approach**: Use a state machine to track whether we're inside a chart tag, parsing text, or in an incomplete state
|
|
41
|
+
3. **Deferred Rendering**: Charts render only when the complete `<chart>...</chart>` tag is received
|
|
42
|
+
4. **Placeholder System**: Show loading skeleton while chart data is streaming
|
|
43
|
+
5. **Chart Instance Management**: Track Chart.js instances for proper cleanup to prevent memory leaks
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Data Flow
|
|
48
|
+
|
|
49
|
+
### Streaming Flow (Detailed)
|
|
50
|
+
|
|
51
|
+
1. **Token arrives** via SSE `token` event in `Bot.tsx`
|
|
52
|
+
2. **ChartTagParser.processToken(token)** analyzes the token:
|
|
53
|
+
- If currently inside a chart tag: buffer the token
|
|
54
|
+
- If token starts a chart tag: start buffering, emit placeholder ID
|
|
55
|
+
- If token completes a chart tag: parse and emit chart config
|
|
56
|
+
- Otherwise: pass through as regular text
|
|
57
|
+
3. **Regular text** flows to existing `addToStreamBuffer` → `processTextBuffer` pipeline
|
|
58
|
+
4. **Chart placeholders** are inserted as special markers: `<!--chart:uuid-->`
|
|
59
|
+
5. **BotBubble.updateBotMessageContent()** is modified to:
|
|
60
|
+
- Parse markdown as usual
|
|
61
|
+
- Detect chart placeholder markers
|
|
62
|
+
- Replace markers with chart container elements
|
|
63
|
+
- Initialize Chart.js instances for completed charts
|
|
64
|
+
|
|
65
|
+
### State Machine States
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
IDLE → OPENING_TAG → INSIDE_TAG → CLOSING_TAG → IDLE
|
|
69
|
+
↓ ↓ ↓
|
|
70
|
+
(partial) (buffering) (partial)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## New Components and Utilities
|
|
76
|
+
|
|
77
|
+
### 1. `src/utils/chartTagParser.ts`
|
|
78
|
+
|
|
79
|
+
**Purpose**: Parse and extract chart XML tags from streaming tokens
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Core interfaces
|
|
83
|
+
interface ChartConfig {
|
|
84
|
+
id: string;
|
|
85
|
+
type: 'line' | 'bar' | 'pie' | 'doughnut' | 'radar' | 'polarArea' | 'bubble' | 'scatter';
|
|
86
|
+
title: string;
|
|
87
|
+
data: ChartData;
|
|
88
|
+
options?: ChartOptions;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface ParseResult {
|
|
92
|
+
textContent: string; // Regular text to render
|
|
93
|
+
chartPlaceholders: string[]; // Chart placeholder IDs added
|
|
94
|
+
completedCharts: ChartConfig[]; // Fully parsed charts
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class ChartTagParser {
|
|
98
|
+
// Parser state
|
|
99
|
+
private state: ParserState;
|
|
100
|
+
private buffer: string;
|
|
101
|
+
private pendingCharts: Map<string, Partial<ChartConfig>>;
|
|
102
|
+
|
|
103
|
+
// Main method called for each token
|
|
104
|
+
processToken(token: string): ParseResult;
|
|
105
|
+
|
|
106
|
+
// Flush any remaining buffer (called on stream end)
|
|
107
|
+
flush(): ParseResult;
|
|
108
|
+
|
|
109
|
+
// Reset parser state (called on new message)
|
|
110
|
+
reset(): void;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Key Features**:
|
|
115
|
+
- Handles tokens split across tag boundaries
|
|
116
|
+
- Generates unique IDs for each chart placeholder
|
|
117
|
+
- Validates JSON configuration before parsing
|
|
118
|
+
- Supports multiple charts in a single message
|
|
119
|
+
|
|
120
|
+
### 2. `src/components/bubbles/ChartBubble.tsx`
|
|
121
|
+
|
|
122
|
+
**Purpose**: SolidJS component that renders a Chart.js chart
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface ChartBubbleProps {
|
|
126
|
+
config: ChartConfig;
|
|
127
|
+
backgroundColor?: string;
|
|
128
|
+
onDestroy?: () => void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Component responsibilities:
|
|
132
|
+
// - Creates canvas element
|
|
133
|
+
// - Initializes Chart.js instance
|
|
134
|
+
// - Handles responsive sizing
|
|
135
|
+
// - Cleans up on unmount
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 3. `src/components/bubbles/ChartLoadingPlaceholder.tsx`
|
|
139
|
+
|
|
140
|
+
**Purpose**: Loading skeleton shown while chart data streams
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
interface ChartLoadingPlaceholderProps {
|
|
144
|
+
chartId: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Shows animated skeleton with:
|
|
148
|
+
// - Chart icon
|
|
149
|
+
// - "Loading chart..." text
|
|
150
|
+
// - Pulsing animation
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4. `src/utils/chartInstanceManager.ts`
|
|
154
|
+
|
|
155
|
+
**Purpose**: Manage Chart.js instances lifecycle to prevent memory leaks
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
class ChartInstanceManager {
|
|
159
|
+
private instances: Map<string, Chart>;
|
|
160
|
+
|
|
161
|
+
register(id: string, chart: Chart): void;
|
|
162
|
+
destroy(id: string): void;
|
|
163
|
+
destroyAll(): void;
|
|
164
|
+
get(id: string): Chart | undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Singleton instance exported for global access
|
|
168
|
+
export const chartManager = new ChartInstanceManager();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Modifications to Existing Components
|
|
174
|
+
|
|
175
|
+
### 1. `src/components/Bot.tsx`
|
|
176
|
+
|
|
177
|
+
**Changes Required**:
|
|
178
|
+
|
|
179
|
+
#### a. Import ChartTagParser
|
|
180
|
+
```typescript
|
|
181
|
+
import { ChartTagParser, ChartConfig } from '@/utils/chartTagParser';
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### b. Add chart parser instance and state
|
|
185
|
+
```typescript
|
|
186
|
+
// Add near other streaming state variables (around line 250)
|
|
187
|
+
let chartParser: ChartTagParser | null = null;
|
|
188
|
+
const [pendingCharts, setPendingCharts] = createSignal<Map<string, Partial<ChartConfig>>>(new Map());
|
|
189
|
+
const [completedCharts, setCompletedCharts] = createSignal<Map<string, ChartConfig>>(new Map());
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### c. Modify token handling in `fetchResponseFromEventStream`
|
|
193
|
+
```typescript
|
|
194
|
+
case 'token':
|
|
195
|
+
setCurrentStatusMsg('');
|
|
196
|
+
|
|
197
|
+
// Process token through chart parser first
|
|
198
|
+
if (!chartParser) chartParser = new ChartTagParser();
|
|
199
|
+
const parseResult = chartParser.processToken(payload.data);
|
|
200
|
+
|
|
201
|
+
// Handle regular text content
|
|
202
|
+
if (parseResult.textContent) {
|
|
203
|
+
addToStreamBuffer(parseResult.textContent);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Track new chart placeholders
|
|
207
|
+
parseResult.chartPlaceholders.forEach(id => {
|
|
208
|
+
setPendingCharts(prev => new Map(prev).set(id, { id }));
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Handle completed charts
|
|
212
|
+
parseResult.completedCharts.forEach(chart => {
|
|
213
|
+
setCompletedCharts(prev => new Map(prev).set(chart.id, chart));
|
|
214
|
+
setPendingCharts(prev => {
|
|
215
|
+
const next = new Map(prev);
|
|
216
|
+
next.delete(chart.id);
|
|
217
|
+
return next;
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
break;
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### d. Modify 'start' event to reset parser
|
|
224
|
+
```typescript
|
|
225
|
+
case 'start':
|
|
226
|
+
// ... existing code ...
|
|
227
|
+
chartParser = new ChartTagParser(); // Reset parser for new message
|
|
228
|
+
setPendingCharts(new Map());
|
|
229
|
+
setCompletedCharts(new Map());
|
|
230
|
+
break;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### e. Modify 'end' event to flush parser
|
|
234
|
+
```typescript
|
|
235
|
+
case 'end':
|
|
236
|
+
// Flush any remaining chart parser content
|
|
237
|
+
if (chartParser) {
|
|
238
|
+
const flushResult = chartParser.flush();
|
|
239
|
+
if (flushResult.textContent) {
|
|
240
|
+
addToStreamBuffer(flushResult.textContent);
|
|
241
|
+
}
|
|
242
|
+
// Handle any final completed charts
|
|
243
|
+
flushResult.completedCharts.forEach(chart => {
|
|
244
|
+
setCompletedCharts(prev => new Map(prev).set(chart.id, chart));
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// ... existing code ...
|
|
248
|
+
break;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### f. Pass chart data to BotBubble
|
|
252
|
+
```typescript
|
|
253
|
+
<BotBubble
|
|
254
|
+
// ... existing props ...
|
|
255
|
+
pendingCharts={pendingCharts()}
|
|
256
|
+
completedCharts={completedCharts()}
|
|
257
|
+
/>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 2. `src/components/bubbles/BotBubble.tsx`
|
|
261
|
+
|
|
262
|
+
**Changes Required**:
|
|
263
|
+
|
|
264
|
+
#### a. Add new props
|
|
265
|
+
```typescript
|
|
266
|
+
type Props = {
|
|
267
|
+
// ... existing props ...
|
|
268
|
+
pendingCharts?: Map<string, Partial<ChartConfig>>;
|
|
269
|
+
completedCharts?: Map<string, ChartConfig>;
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### b. Modify updateBotMessageContent function
|
|
274
|
+
```typescript
|
|
275
|
+
function updateBotMessageContent(newMessage: string) {
|
|
276
|
+
if (botMessageEl) {
|
|
277
|
+
const safeMessage = convertEmailToMarkdownLink(newMessage);
|
|
278
|
+
let htmlContent = Marked.parse(safeMessage);
|
|
279
|
+
|
|
280
|
+
// Replace chart placeholders with actual chart containers
|
|
281
|
+
htmlContent = replaceChartPlaceholders(htmlContent, props.completedCharts, props.pendingCharts);
|
|
282
|
+
|
|
283
|
+
botMessageEl.innerHTML = htmlContent;
|
|
284
|
+
|
|
285
|
+
// Initialize Chart.js instances for completed charts
|
|
286
|
+
initializeCharts(botMessageEl, props.completedCharts);
|
|
287
|
+
|
|
288
|
+
botMessageEl.querySelectorAll('a').forEach((link) => {
|
|
289
|
+
link.target = '_blank';
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### c. Add chart initialization and cleanup
|
|
296
|
+
```typescript
|
|
297
|
+
import { chartManager } from '@/utils/chartInstanceManager';
|
|
298
|
+
|
|
299
|
+
// In component cleanup
|
|
300
|
+
onCleanup(() => {
|
|
301
|
+
// Destroy all chart instances for this bubble
|
|
302
|
+
props.completedCharts?.forEach((_, id) => {
|
|
303
|
+
chartManager.destroy(id);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Implementation Steps
|
|
311
|
+
|
|
312
|
+
### Phase 1: Setup and Dependencies (Priority: High)
|
|
313
|
+
1. **Add Chart.js dependency**: `yarn add chart.js`
|
|
314
|
+
2. **Create type definitions** for chart configurations
|
|
315
|
+
3. **Create chartTagParser.ts** with basic parsing logic
|
|
316
|
+
|
|
317
|
+
### Phase 2: Core Parser Implementation (Priority: High)
|
|
318
|
+
4. **Implement state machine** in ChartTagParser
|
|
319
|
+
5. **Handle edge cases**: partial tags, malformed JSON, nested content
|
|
320
|
+
6. **Write unit tests** for parser
|
|
321
|
+
|
|
322
|
+
### Phase 3: Chart Rendering Components (Priority: High)
|
|
323
|
+
7. **Create ChartBubble.tsx** component
|
|
324
|
+
8. **Create ChartLoadingPlaceholder.tsx** component
|
|
325
|
+
9. **Create chartInstanceManager.ts** utility
|
|
326
|
+
|
|
327
|
+
### Phase 4: Integration with Streaming (Priority: High)
|
|
328
|
+
10. **Modify Bot.tsx** to integrate ChartTagParser
|
|
329
|
+
11. **Modify BotBubble.tsx** to render charts
|
|
330
|
+
12. **Test with live SSE streaming**
|
|
331
|
+
|
|
332
|
+
### Phase 5: Styling and Polish (Priority: Medium)
|
|
333
|
+
13. **Add responsive chart sizing**
|
|
334
|
+
14. **Style chart containers** to match chat UI
|
|
335
|
+
15. **Add error states** for failed chart rendering
|
|
336
|
+
|
|
337
|
+
### Phase 6: Testing and Edge Cases (Priority: High)
|
|
338
|
+
16. **Test multiple charts** in single message
|
|
339
|
+
17. **Test chart + text** mixed content
|
|
340
|
+
18. **Test memory cleanup** on message updates
|
|
341
|
+
19. **Test with various chart types**
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Edge Cases and Error Handling
|
|
346
|
+
|
|
347
|
+
### Edge Case 1: Incomplete Chart Tag at Stream End
|
|
348
|
+
**Scenario**: Stream ends while inside a chart tag
|
|
349
|
+
**Solution**: `flush()` method treats incomplete tag as plain text
|
|
350
|
+
```typescript
|
|
351
|
+
flush(): ParseResult {
|
|
352
|
+
if (this.state !== ParserState.IDLE) {
|
|
353
|
+
// Emit buffered content as plain text
|
|
354
|
+
return { textContent: this.buffer, chartPlaceholders: [], completedCharts: [] };
|
|
355
|
+
}
|
|
356
|
+
return { textContent: '', chartPlaceholders: [], completedCharts: [] };
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Edge Case 2: Malformed JSON in Chart Tag
|
|
361
|
+
**Scenario**: Chart tag contains invalid JSON
|
|
362
|
+
**Solution**: Render error message inline instead of chart
|
|
363
|
+
```typescript
|
|
364
|
+
try {
|
|
365
|
+
const config = JSON.parse(jsonContent);
|
|
366
|
+
return { type: 'chart', config };
|
|
367
|
+
} catch (e) {
|
|
368
|
+
return { type: 'error', message: 'Invalid chart configuration' };
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Edge Case 3: Token Splits Tag Boundary
|
|
373
|
+
**Scenario**: Token contains partial opening/closing tag
|
|
374
|
+
```
|
|
375
|
+
Token 1: "Here is the data: <cha"
|
|
376
|
+
Token 2: "rt type=\"line\">"
|
|
377
|
+
Token 3: "{\"labels\": [...]}"
|
|
378
|
+
Token 4: "</chart>"
|
|
379
|
+
```
|
|
380
|
+
**Solution**: State machine buffers partial tags until complete
|
|
381
|
+
|
|
382
|
+
### Edge Case 4: Nested or Escaped Content
|
|
383
|
+
**Scenario**: JSON contains `</chart>` as string value
|
|
384
|
+
**Solution**: Track brace depth to find true closing tag
|
|
385
|
+
```typescript
|
|
386
|
+
// Count braces to find actual JSON end
|
|
387
|
+
let braceDepth = 0;
|
|
388
|
+
for (let i = 0; i < content.length; i++) {
|
|
389
|
+
if (content[i] === '{') braceDepth++;
|
|
390
|
+
if (content[i] === '}') braceDepth--;
|
|
391
|
+
if (braceDepth === 0) {
|
|
392
|
+
// Found JSON end
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Edge Case 5: Multiple Charts Back-to-Back
|
|
398
|
+
**Scenario**: `<chart>...</chart><chart>...</chart>`
|
|
399
|
+
**Solution**: Parser resets to IDLE after each complete tag
|
|
400
|
+
|
|
401
|
+
### Edge Case 6: Chart in Middle of Text
|
|
402
|
+
**Scenario**: "Here's the chart: <chart>...</chart> And here's more text."
|
|
403
|
+
**Solution**: Emit text before chart, placeholder, then text after
|
|
404
|
+
|
|
405
|
+
### Edge Case 7: Memory Leaks from Chart Instances
|
|
406
|
+
**Scenario**: Charts created but never destroyed
|
|
407
|
+
**Solution**: ChartInstanceManager tracks all instances, cleanup on:
|
|
408
|
+
- Component unmount
|
|
409
|
+
- Message update (chart replaced)
|
|
410
|
+
- Chat clear
|
|
411
|
+
|
|
412
|
+
### Edge Case 8: Rapid Streaming Updates
|
|
413
|
+
**Scenario**: Chart placeholder created, then immediately completed
|
|
414
|
+
**Solution**: Debounce chart initialization, check if placeholder still exists
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Testing Strategy
|
|
419
|
+
|
|
420
|
+
### Unit Tests
|
|
421
|
+
|
|
422
|
+
1. **ChartTagParser Tests**
|
|
423
|
+
- Parse complete chart tag in single token
|
|
424
|
+
- Parse chart tag split across multiple tokens
|
|
425
|
+
- Handle malformed JSON gracefully
|
|
426
|
+
- Handle multiple charts in sequence
|
|
427
|
+
- Handle chart embedded in text
|
|
428
|
+
- Handle incomplete tags on flush
|
|
429
|
+
|
|
430
|
+
2. **ChartInstanceManager Tests**
|
|
431
|
+
- Register and retrieve instances
|
|
432
|
+
- Destroy individual instance
|
|
433
|
+
- Destroy all instances
|
|
434
|
+
- Handle destroy of non-existent instance
|
|
435
|
+
|
|
436
|
+
### Integration Tests
|
|
437
|
+
|
|
438
|
+
1. **Streaming Simulation**
|
|
439
|
+
- Simulate SSE tokens with chart tags
|
|
440
|
+
- Verify placeholder appears during streaming
|
|
441
|
+
- Verify chart renders on completion
|
|
442
|
+
|
|
443
|
+
2. **BotBubble Rendering**
|
|
444
|
+
- Render message with chart
|
|
445
|
+
- Render message with multiple charts
|
|
446
|
+
- Render mixed text and charts
|
|
447
|
+
- Verify chart cleanup on unmount
|
|
448
|
+
|
|
449
|
+
### Manual Testing Checklist
|
|
450
|
+
|
|
451
|
+
- [ ] Single line chart renders correctly
|
|
452
|
+
- [ ] Bar chart renders correctly
|
|
453
|
+
- [ ] Pie/doughnut chart renders correctly
|
|
454
|
+
- [ ] Chart title displays correctly
|
|
455
|
+
- [ ] Chart is responsive (resizes with container)
|
|
456
|
+
- [ ] Multiple charts in one message
|
|
457
|
+
- [ ] Chart between paragraphs of text
|
|
458
|
+
- [ ] Loading placeholder shows during streaming
|
|
459
|
+
- [ ] Error message for invalid chart config
|
|
460
|
+
- [ ] No console errors on chat clear
|
|
461
|
+
- [ ] No memory leaks (check dev tools)
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Chart XML Tag Format Reference
|
|
466
|
+
|
|
467
|
+
### Basic Structure
|
|
468
|
+
```xml
|
|
469
|
+
<chart type="line" title="Chart Title">
|
|
470
|
+
{
|
|
471
|
+
"labels": ["Label1", "Label2", ...],
|
|
472
|
+
"datasets": [
|
|
473
|
+
{
|
|
474
|
+
"label": "Dataset Label",
|
|
475
|
+
"data": [1, 2, 3, ...],
|
|
476
|
+
"backgroundColor": "rgba(54, 162, 235, 0.2)",
|
|
477
|
+
"borderColor": "rgba(54, 162, 235, 1)",
|
|
478
|
+
"borderWidth": 2
|
|
479
|
+
}
|
|
480
|
+
],
|
|
481
|
+
"options": {
|
|
482
|
+
"responsive": true,
|
|
483
|
+
"plugins": {
|
|
484
|
+
"legend": { "position": "top" }
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
</chart>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Supported Chart Types
|
|
492
|
+
- `line` - Line chart
|
|
493
|
+
- `bar` - Bar chart
|
|
494
|
+
- `pie` - Pie chart
|
|
495
|
+
- `doughnut` - Doughnut chart
|
|
496
|
+
- `radar` - Radar chart
|
|
497
|
+
- `polarArea` - Polar area chart
|
|
498
|
+
- `bubble` - Bubble chart
|
|
499
|
+
- `scatter` - Scatter chart
|
|
500
|
+
|
|
501
|
+
### Required Fields
|
|
502
|
+
- `type` attribute (chart type)
|
|
503
|
+
- `labels` array in JSON body
|
|
504
|
+
- `datasets` array with at least one dataset
|
|
505
|
+
- Each dataset must have `data` array
|
|
506
|
+
|
|
507
|
+
### Optional Fields
|
|
508
|
+
- `title` attribute (displayed above chart)
|
|
509
|
+
- `options` object (Chart.js options)
|
|
510
|
+
- Dataset styling properties (colors, borders, etc.)
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Dependencies to Add
|
|
515
|
+
|
|
516
|
+
```json
|
|
517
|
+
{
|
|
518
|
+
"dependencies": {
|
|
519
|
+
"chart.js": "^4.4.0"
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Installation Command**: `yarn add chart.js`
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## File Structure After Implementation
|
|
529
|
+
|
|
530
|
+
```
|
|
531
|
+
src/
|
|
532
|
+
├── components/
|
|
533
|
+
│ ├── bubbles/
|
|
534
|
+
│ │ ├── BotBubble.tsx (modified)
|
|
535
|
+
│ │ ├── ChartBubble.tsx (new)
|
|
536
|
+
│ │ └── ChartLoadingPlaceholder.tsx (new)
|
|
537
|
+
│ └── Bot.tsx (modified)
|
|
538
|
+
├── utils/
|
|
539
|
+
│ ├── chartTagParser.ts (new)
|
|
540
|
+
│ ├── chartInstanceManager.ts (new)
|
|
541
|
+
│ └── index.ts
|
|
542
|
+
└── types/
|
|
543
|
+
└── chart.ts (new - type definitions)
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Performance Considerations
|
|
549
|
+
|
|
550
|
+
1. **Lazy Loading**: Consider lazy-loading Chart.js to reduce initial bundle size
|
|
551
|
+
2. **Canvas Optimization**: Use `devicePixelRatio` option for crisp rendering
|
|
552
|
+
3. **Animation**: Disable animations during streaming for smoother experience
|
|
553
|
+
4. **Debouncing**: Debounce chart updates if data changes rapidly
|
|
554
|
+
5. **Memory**: Always destroy Chart instances when no longer needed
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Future Enhancements (Out of Scope)
|
|
559
|
+
|
|
560
|
+
1. **Interactive Charts**: Click handlers, tooltips, zoom
|
|
561
|
+
2. **Chart Export**: Download as PNG/SVG
|
|
562
|
+
3. **Chart Editing**: Allow users to modify chart after rendering
|
|
563
|
+
4. **More Chart Types**: Support for additional Chart.js plugins
|
|
564
|
+
5. **Real-time Updates**: Support for updating chart data after initial render
|
|
565
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cmcts-c-agent-embedding",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29-cagent",
|
|
4
4
|
"description": "Javascript library to display flowise chatbot on your website",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "cross-env NODE_ENV=development rollup --watch --config rollup.config.js",
|
|
10
10
|
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
|
|
11
|
-
"
|
|
12
|
-
"lint
|
|
11
|
+
"type-check": "tsc --noEmit",
|
|
12
|
+
"lint": "eslint --quiet \"src/**/*.ts*\"",
|
|
13
|
+
"lint-fix": "eslint --quiet --fix \"src/**/*.ts*\"",
|
|
13
14
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx}\"",
|
|
14
15
|
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,md,mdx}\"",
|
|
15
16
|
"prepare": "husky install",
|
|
16
17
|
"start": "node server.js",
|
|
17
|
-
"build-and-publish": "yarn build && npm
|
|
18
|
+
"build-and-publish": "yarn build && npm publish",
|
|
19
|
+
"prepublishOnly": "yarn build"
|
|
18
20
|
},
|
|
19
21
|
"license": "MIT",
|
|
20
22
|
"dependencies": {
|
|
@@ -25,6 +27,8 @@
|
|
|
25
27
|
"axios": "^1.7.7",
|
|
26
28
|
"c-agent-embedding": "^1.0.4",
|
|
27
29
|
"c-agent-embedding-react": "^1.0.1",
|
|
30
|
+
"chart.js": "^4.5.1",
|
|
31
|
+
"chartjs-plugin-datalabels": "^2.2.0",
|
|
28
32
|
"cors": "^2.8.5",
|
|
29
33
|
"cross-env": "^7.0.3",
|
|
30
34
|
"device-detector-js": "^3.0.3",
|
|
@@ -77,4 +81,4 @@
|
|
|
77
81
|
"uuid": "^9.0.1"
|
|
78
82
|
},
|
|
79
83
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
80
|
-
}
|
|
84
|
+
}
|