teko-chat-sdk 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/README.md +462 -0
- package/dist/index.cjs +1759 -0
- package/dist/index.d.cts +175 -0
- package/dist/index.d.ts +175 -0
- package/dist/index.js +1722 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
# TekoChatSDK
|
|
2
|
+
|
|
3
|
+
React component library giúp các ECOM app của Teko tích hợp AI chatbot với khả năng **bidirectional communication** — right panel có thể push context ngược lại cho AI.
|
|
4
|
+
|
|
5
|
+
## Tính năng
|
|
6
|
+
|
|
7
|
+
- **3 UI states**: `bubble` → `mini` popup → `fullscreen` split layout (35/65)
|
|
8
|
+
- **Mobile mode**: `layoutMode="mobile"` — mini popup chuyển thành bottom sheet full-width; fullscreen chuyển thành single-column với floating toggle icon + right panel bottom sheet overlay
|
|
9
|
+
- **Streaming responses**: AI response xuất hiện dần theo chunks (typewriter effect)
|
|
10
|
+
- **Pluggable transport**: Auto-detect giao thức từ `chatBffUrl` scheme — WebSocket (`wss://`), HTTP Streaming (`https://`), hoặc mock built-in (không có URL)
|
|
11
|
+
- **Bidirectional context**: Right-side app UI push context ngược về AI qua `ref.sendContext()`
|
|
12
|
+
- **Intent-driven UI**: BFF trả về intent → SDK tự điều phối giao diện
|
|
13
|
+
- **Quick reply**: Suggest options từ BFF hiển thị dưới dạng clickable buttons
|
|
14
|
+
- **Mock handler**: Không cần backend thật, truyền `chatBffUrl=""` để dùng mock built-in với streaming giả lập
|
|
15
|
+
- **Debug panel**: `onDebugEvent` callback theo dõi toàn bộ request/response/intent/context
|
|
16
|
+
- **Customizable bubble**: Truyền `renderBubble` để dùng UI bubble tùy chỉnh
|
|
17
|
+
|
|
18
|
+
## Cài đặt
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
yarn add teko-chat-sdk
|
|
22
|
+
# hoặc
|
|
23
|
+
npm install teko-chat-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Peer dependencies:**
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add react react-dom
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { useRef } from 'react';
|
|
36
|
+
import { TekoChatWidget } from 'teko-chat-sdk';
|
|
37
|
+
import type { TekoChatWidgetRef } from 'teko-chat-sdk';
|
|
38
|
+
|
|
39
|
+
export default function App() {
|
|
40
|
+
const chatRef = useRef<TekoChatWidgetRef>(null);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<YourApp />
|
|
45
|
+
|
|
46
|
+
<TekoChatWidget
|
|
47
|
+
ref={chatRef}
|
|
48
|
+
appId="your-app-id"
|
|
49
|
+
chatBffUrl="https://your-bff.example.com/chat"
|
|
50
|
+
onIntent={(action, componentKey) => {
|
|
51
|
+
if (action === 'show_ui' && componentKey) {
|
|
52
|
+
chatRef.current?.openFullscreen(componentKey);
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
renderRightPanel={(componentKey) => {
|
|
56
|
+
if (componentKey === 'cart') return <CartPage />;
|
|
57
|
+
return null;
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API
|
|
66
|
+
|
|
67
|
+
### `<TekoChatWidget>` Props
|
|
68
|
+
|
|
69
|
+
**Core**
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Required | Mô tả |
|
|
72
|
+
| ------------ | -------- | -------- | ------------------------------------------------------------------------- |
|
|
73
|
+
| `appId` | `string` | ✅ | App identifier, gửi lên BFF để phân biệt context |
|
|
74
|
+
| `chatBffUrl` | `string` | ✅ | BFF endpoint. `wss://` → WebSocket, `https://` → HTTP stream, `""` → mock |
|
|
75
|
+
|
|
76
|
+
**Business logic**
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Mô tả |
|
|
79
|
+
| ------------------ | ------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
80
|
+
| `onIntent` | `(action: string, componentKey?: string) => void` | Callback khi BFF trả về intent action |
|
|
81
|
+
| `renderRightPanel` | `(componentKey: string) => ReactNode` | Render content cho right panel ở fullscreen state |
|
|
82
|
+
| `getAppContext` | `() => Record<string, unknown> \| Promise<...>` | Inject app metadata vào mỗi BFF request, gọi tại send-time (luôn fresh) |
|
|
83
|
+
|
|
84
|
+
**UI customization**
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Mô tả |
|
|
87
|
+
| -------------- | -------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------- |
|
|
88
|
+
| `renderBubble` | `(onClick: () => void) => ReactNode` | — | Custom bubble UI, thay thế bubble mặc định |
|
|
89
|
+
| `bubbleAnchor` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Vị trí bubble, ảnh hưởng hướng animation fullscreen |
|
|
90
|
+
| `layoutMode` | `'desktop' \| 'mobile'` | `'desktop'` | Desktop: mini popup + split fullscreen. Mobile: bottom sheet + single column |
|
|
91
|
+
| `primaryColor` | `string` | `'#1a73e8'` | Primary color (hex) cho toàn bộ widget |
|
|
92
|
+
| `botAvatar` | `string` | — | Avatar URL cho AI bubble |
|
|
93
|
+
| `locale` | `'vi' \| 'en'` | `'vi'` | Ngôn ngữ hiển thị |
|
|
94
|
+
| `labels` | `Partial<ChatLabels>` | — | Override từng label cụ thể, kết hợp được với `locale` |
|
|
95
|
+
|
|
96
|
+
**Layout / spacing**
|
|
97
|
+
|
|
98
|
+
| Prop | Type | Default | Mô tả |
|
|
99
|
+
| -------------- | -------- | ------- | -------------------------------------------------- |
|
|
100
|
+
| `offsetTop` | `number` | `0` | Offset từ top (px) — tránh che fixed header |
|
|
101
|
+
| `offsetBottom` | `number` | `0` | Offset từ bottom (px) — tránh che fixed bottom nav |
|
|
102
|
+
| `miniWidth` | `number` | `360` | Chiều rộng mini popup (px) |
|
|
103
|
+
| `miniHeight` | `number` | `480` | Chiều cao mini popup (px) |
|
|
104
|
+
| `zIndex` | `number` | `1031` | CSS z-index. Fullscreen backdrop = zIndex + 9 |
|
|
105
|
+
|
|
106
|
+
**Dev only**
|
|
107
|
+
|
|
108
|
+
| Prop | Type | Mô tả |
|
|
109
|
+
| -------------- | ----------------------------- | --------------------------- |
|
|
110
|
+
| `onDebugEvent` | `(event: DebugEvent) => void` | Nhận mọi SDK event để debug |
|
|
111
|
+
|
|
112
|
+
### `TekoChatWidgetRef` Methods
|
|
113
|
+
|
|
114
|
+
Truy cập qua `ref.current`:
|
|
115
|
+
|
|
116
|
+
| Method | Signature | Mô tả |
|
|
117
|
+
| ----------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `open` | `() => void` | Mở mini popup |
|
|
119
|
+
| `close` | `() => void` | Đóng chat, về bubble state |
|
|
120
|
+
| `openFullscreen` | `(componentKey: string) => void` | Mở fullscreen split với component key |
|
|
121
|
+
| `closeFullscreen` | `() => void` | Thu nhỏ về mini popup |
|
|
122
|
+
| `sendMessage` | `(text: string, context?: Record<string, unknown>) => void` | Gửi message lên BFF programmatically; `context` được forward kèm request (dùng cho quick reply payload) |
|
|
123
|
+
| `sendContext` | `(data: Record<string, unknown>) => void` | Push context từ right panel ngược về AI |
|
|
124
|
+
|
|
125
|
+
## Customization
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
<TekoChatWidget
|
|
129
|
+
appId="your-app-id"
|
|
130
|
+
chatBffUrl="https://your-bff.example.com/chat"
|
|
131
|
+
primaryColor="#e53e3e" // thay màu chủ đạo
|
|
132
|
+
zIndex={2000} // tăng z-index nếu app có overlay khác
|
|
133
|
+
offsetTop={64} // chừa 64px cho fixed header
|
|
134
|
+
offsetBottom={56} // chừa 56px cho bottom navigation bar
|
|
135
|
+
miniWidth={400}
|
|
136
|
+
miniHeight={520}
|
|
137
|
+
/>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Mobile Mode
|
|
141
|
+
|
|
142
|
+
SDK không tự detect viewport — consumer app tự detect và truyền `layoutMode` vào:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
function App() {
|
|
146
|
+
const [layoutMode, setLayoutMode] = useState<'desktop' | 'mobile'>(
|
|
147
|
+
() => (window.innerWidth < 768 ? 'mobile' : 'desktop'),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
const handler = () =>
|
|
152
|
+
setLayoutMode(window.innerWidth < 768 ? 'mobile' : 'desktop');
|
|
153
|
+
window.addEventListener('resize', handler);
|
|
154
|
+
return () => window.removeEventListener('resize', handler);
|
|
155
|
+
}, []);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<TekoChatWidget
|
|
159
|
+
layoutMode={layoutMode}
|
|
160
|
+
renderRightPanel={(key) =>
|
|
161
|
+
layoutMode === 'mobile' ? <MobileCartPage /> : <DesktopCartPage />
|
|
162
|
+
}
|
|
163
|
+
// ...
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Mobile behavior:**
|
|
170
|
+
- `mini` state: full-width bottom sheet slide up từ đáy (thay vì popup 360px); click vùng tối bên ngoài để đóng
|
|
171
|
+
- `fullscreen` state: chat luôn hiển thị full-screen; khi BFF trigger `show_ui`, right panel slide up dạng bottom sheet overlay phía trên chat; click vùng tối để đóng overlay về chat; tap floating toggle icon để mở lại right panel
|
|
172
|
+
|
|
173
|
+
## Bidirectional Communication
|
|
174
|
+
|
|
175
|
+
Điểm khác biệt chính của SDK: right panel có thể push context ngược về AI để cải thiện intent detection.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
// App.tsx
|
|
179
|
+
<TekoChatWidget
|
|
180
|
+
ref={chatRef}
|
|
181
|
+
renderRightPanel={(componentKey) => (
|
|
182
|
+
<CartPage
|
|
183
|
+
onContextUpdate={(data) => chatRef.current?.sendContext(data)}
|
|
184
|
+
/>
|
|
185
|
+
)}
|
|
186
|
+
/>
|
|
187
|
+
|
|
188
|
+
// CartPage.tsx
|
|
189
|
+
function CartPage({ onContextUpdate }) {
|
|
190
|
+
const handleQtyChange = (qty: number) => {
|
|
191
|
+
updateCart(qty);
|
|
192
|
+
onContextUpdate({
|
|
193
|
+
type: 'cart_updated',
|
|
194
|
+
items: cartItems,
|
|
195
|
+
totalAmount: calculateTotal(),
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## BFF Protocol
|
|
202
|
+
|
|
203
|
+
SDK tự chọn giao thức dựa vào scheme của `chatBffUrl`:
|
|
204
|
+
|
|
205
|
+
| `chatBffUrl` | Giao thức | Ghi chú |
|
|
206
|
+
| ------------------------------- | -------------- | --------------------------------------------------------------------- |
|
|
207
|
+
| `wss://...` hoặc `ws://...` | WebSocket | Persistent connection, BFF push `TransportFrame` JSON qua `ws.send()` |
|
|
208
|
+
| `https://...` hoặc `http://...` | HTTP Streaming | POST request, BFF trả newline-delimited JSON (`application/x-ndjson`) |
|
|
209
|
+
| `""` hoặc không truyền | Mock handler | Built-in, không cần backend — simulate streaming word-by-word |
|
|
210
|
+
|
|
211
|
+
### TransportFrame (BFF → SDK streaming format)
|
|
212
|
+
|
|
213
|
+
BFF cần gửi các frames theo format sau (mỗi frame là một JSON object):
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
type TransportFrame =
|
|
217
|
+
| { type: 'chunk'; text: string } // một đoạn text AI đang generate
|
|
218
|
+
| { type: 'done'; data: BFFResponseData } // kết thúc, kèm full response data
|
|
219
|
+
| { type: 'error'; message: string } // lỗi
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**WebSocket:** Mỗi `ws.send()` là một frame JSON.
|
|
223
|
+
|
|
224
|
+
**HTTP Streaming:** Mỗi dòng (`\n`-delimited) là một frame JSON.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## BFF JSON Contract
|
|
229
|
+
|
|
230
|
+
### BFF → SDK (Response)
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"code": 200,
|
|
235
|
+
"message": "ok",
|
|
236
|
+
"data": {
|
|
237
|
+
"conversationId": "conv-abc123",
|
|
238
|
+
"message": "Bạn muốn thêm loại nào?",
|
|
239
|
+
"intent": "addToCart",
|
|
240
|
+
"action": "show_ui",
|
|
241
|
+
"componentKey": "cart",
|
|
242
|
+
"suggest": {
|
|
243
|
+
"options": [
|
|
244
|
+
{ "key": "suon-non", "label": "Sườn non", "payload": { "sku": "SN-001" } },
|
|
245
|
+
{ "key": "ga-dong-tao", "label": "Gà đông tảo", "payload": { "sku": "GA-001" } }
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
"timestamp": 1742208605000
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**`action` values SDK xử lý:**
|
|
254
|
+
- `show_ui` — SDK gọi `renderRightPanel(componentKey)` và chuyển sang fullscreen
|
|
255
|
+
- Các value khác — SDK forward sang `onIntent` callback để app tự xử lý
|
|
256
|
+
|
|
257
|
+
### SDK → BFF (Request)
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
// Nhắn tin thường
|
|
261
|
+
{ "conversationId": "conv-abc123", "message": "tôi muốn mua gà", "timestamp": 1742208600000 }
|
|
262
|
+
|
|
263
|
+
// Click quick reply (có context từ option payload)
|
|
264
|
+
{ "conversationId": "conv-abc123", "message": "Sườn non", "context": { "sku": "SN-001" }, "timestamp": 1742208600000 }
|
|
265
|
+
|
|
266
|
+
// Push context từ right panel (không có message)
|
|
267
|
+
{ "conversationId": "conv-abc123", "context": { "type": "cart_updated", "items": [...] }, "timestamp": 1742208600000 }
|
|
268
|
+
|
|
269
|
+
// Với getAppContext (appContext được inject vào mọi request nếu có giá trị)
|
|
270
|
+
{ "conversationId": "conv-abc123", "message": "tôi muốn mua gà", "appContext": { "terminalId": "T001", "terminalCode": "freshmart-hanoi" }, "timestamp": 1742208600000 }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Release
|
|
274
|
+
|
|
275
|
+
Quy trình release lên NPM qua `release-it` + GitLab CI.
|
|
276
|
+
|
|
277
|
+
### Setup Gitlab project
|
|
278
|
+
|
|
279
|
+
Vào **Settings > CI/CD > Variables**, thêm:
|
|
280
|
+
|
|
281
|
+
| Variable | Value | Protected | Masked |
|
|
282
|
+
| ----------- | ------------------ | --------- | ------ |
|
|
283
|
+
| `NPM_TOKEN` | token từ npmjs.org | x | x |
|
|
284
|
+
|
|
285
|
+
### Quy trình release
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# 1. Merge tất cả feature branches vào main, đảm bảo pass CI
|
|
289
|
+
|
|
290
|
+
# 2. Run các command local
|
|
291
|
+
yarn install
|
|
292
|
+
yarn release
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`yarn release` sẽ:
|
|
296
|
+
|
|
297
|
+
1. Phân tích commit history (Conventional Commits) để gợi ý version bump
|
|
298
|
+
2. Cập nhật `version` trong `package.json`
|
|
299
|
+
3. Tạo entry mới trong `CHANGELOG.md`
|
|
300
|
+
4. Tạo commit `chore(release): vX.Y.Z`
|
|
301
|
+
5. Tạo git tag `vX.Y.Z`
|
|
302
|
+
6. Push commit + tag lên GitLab
|
|
303
|
+
|
|
304
|
+
GitLab CI detect tag `v*.*.*` → tự động chạy job `publish:npm`:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
yarn lint → yarn build → npm publish --access public
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Version bump theo Conventional Commits
|
|
311
|
+
|
|
312
|
+
| Commit prefix | Version bump |
|
|
313
|
+
| --------------------------- | ------------- |
|
|
314
|
+
| `fix:`, `perf:` | patch (0.0.x) |
|
|
315
|
+
| `feat:` | minor (0.x.0) |
|
|
316
|
+
| `BREAKING CHANGE` in footer | major (x.0.0) |
|
|
317
|
+
|
|
318
|
+
### Pre-release (alpha / beta / rc)
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
yarn release --preRelease=alpha # → v1.0.0-alpha.1, npm tag: alpha
|
|
322
|
+
yarn release --preRelease=beta # → v1.0.0-beta.1, npm tag: beta
|
|
323
|
+
yarn release --preRelease=rc # → v1.0.0-rc.1, npm tag: rc
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Consumer cài pre-release bằng cách chỉ định dist-tag:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
npm install teko-chat-sdk@beta
|
|
330
|
+
npm install teko-chat-sdk@alpha
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Package `latest` (stable) không bị ảnh hưởng bởi các lần publish pre-release.
|
|
334
|
+
|
|
335
|
+
### Manual version override
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
yarn release --release-version 1.0.0 # chỉ định version cụ thể
|
|
339
|
+
yarn release minor # ép bump minor
|
|
340
|
+
yarn release patch --dry-run # preview không commit thật
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Checklist trước khi release
|
|
344
|
+
|
|
345
|
+
- [ ] `main` branch CI pass
|
|
346
|
+
- [ ] `yarn lint` và `yarn build` pass local
|
|
347
|
+
- [ ] Working directory sạch (không có uncommitted changes)
|
|
348
|
+
- [ ] `NPM_TOKEN` CI variable đã được set trong GitLab project
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Development
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Clone repo
|
|
356
|
+
git clone <repo-url>
|
|
357
|
+
cd teko-chat-sdk
|
|
358
|
+
|
|
359
|
+
# Cài dependencies
|
|
360
|
+
yarn install
|
|
361
|
+
|
|
362
|
+
# Chạy demo app (localhost:5173)
|
|
363
|
+
yarn dev
|
|
364
|
+
|
|
365
|
+
# Build SDK
|
|
366
|
+
yarn build
|
|
367
|
+
|
|
368
|
+
# Lint
|
|
369
|
+
yarn lint
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Storybook
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Dev server — localhost:6006
|
|
376
|
+
yarn storybook
|
|
377
|
+
|
|
378
|
+
# Build static output
|
|
379
|
+
yarn build-storybook
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Stories có sẵn:**
|
|
383
|
+
|
|
384
|
+
| Story | Mô tả |
|
|
385
|
+
| -------------------- | ------------------------------------------------------------------------------------------------- |
|
|
386
|
+
| `SDK/TekoChatWidget` | Full integration — BubbleState, WithCustomColor, WithOffsets, MiniPopup, MobileMode |
|
|
387
|
+
| `SDK/ChatBubble` | Default, CustomColor, GreenColor, WithOffset |
|
|
388
|
+
| `SDK/ChatMiniPopup` | Empty, WithMessages, WithLoading, CustomColor, CustomSize, MobileBottomSheet |
|
|
389
|
+
| `SDK/ChatFullscreen` | SplitLayout, WithoutRightPanel, CustomColor, WithHeaderOffset, MobileLayout, MobileLayoutChatView |
|
|
390
|
+
| `SDK/DataFlow` | DataFlowInspector — inline event log dùng `onDebugEvent` |
|
|
391
|
+
| `SDK/MessageBubble` | UserMessage, AIMessage, AIMessageWithOptions, CustomColor |
|
|
392
|
+
|
|
393
|
+
### Cấu trúc
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
teko-chat-sdk/
|
|
397
|
+
├── src/
|
|
398
|
+
│ ├── locales/
|
|
399
|
+
│ │ └── index.ts # ChatLabels interface, BUILT_IN_LABELS (vi/en), resolveLabels()
|
|
400
|
+
│ ├── components/
|
|
401
|
+
│ │ ├── TekoChatWidget.tsx # Root component, state machine
|
|
402
|
+
│ │ ├── ChatBubble.tsx # State 1: floating button
|
|
403
|
+
│ │ ├── ChatMiniPopup.tsx # State 2: mini popup
|
|
404
|
+
│ │ ├── ChatFullscreen.tsx # State 3: fullscreen split layout
|
|
405
|
+
│ │ ├── MessageList.tsx
|
|
406
|
+
│ │ ├── MessageBubble.tsx # Message + quick reply options
|
|
407
|
+
│ │ └── MessageInput.tsx
|
|
408
|
+
│ ├── hooks/
|
|
409
|
+
│ │ └── useChatSession.ts # Session management, orchestrates transport
|
|
410
|
+
│ ├── transports/
|
|
411
|
+
│ │ ├── ITransport.ts # ITransport interface + TransportFrame type
|
|
412
|
+
│ │ ├── WebSocketTransport.ts # wss:// — persistent WS, auto-retry
|
|
413
|
+
│ │ ├── HttpStreamTransport.ts # https:// — fetch ndjson streaming
|
|
414
|
+
│ │ ├── MockTransport.ts # dev fallback — wraps mockHandler, simulates chunks
|
|
415
|
+
│ │ └── createTransport.ts # factory: auto-detect từ bffUrl scheme
|
|
416
|
+
│ ├── utils/
|
|
417
|
+
│ │ ├── mockHandler.ts # Mock BFF logic (dùng bởi MockTransport)
|
|
418
|
+
│ │ └── chatTheme.ts # ChatThemeContext, useChatTheme, hexToRgba
|
|
419
|
+
│ ├── stories/ # Storybook stories
|
|
420
|
+
│ │ ├── TekoChatWidget.stories.tsx
|
|
421
|
+
│ │ ├── ChatBubble.stories.tsx
|
|
422
|
+
│ │ ├── ChatMiniPopup.stories.tsx
|
|
423
|
+
│ │ ├── ChatFullscreen.stories.tsx
|
|
424
|
+
│ │ ├── DataFlow.stories.tsx
|
|
425
|
+
│ │ └── MessageBubble.stories.tsx
|
|
426
|
+
│ ├── types.ts # Full type definitions
|
|
427
|
+
│ └── index.ts # Public exports
|
|
428
|
+
├── .storybook/ # Storybook config
|
|
429
|
+
│ ├── main.ts
|
|
430
|
+
│ └── preview.ts
|
|
431
|
+
└── demo/ # Demo app (FreshMart ECOM)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Architecture
|
|
435
|
+
|
|
436
|
+
```
|
|
437
|
+
ECOM App
|
|
438
|
+
│
|
|
439
|
+
└── <TekoChatWidget ref={chatRef} chatBffUrl="..." onIntent={...} renderRightPanel={...} />
|
|
440
|
+
│
|
|
441
|
+
│ createTransport(chatBffUrl) — auto-detect từ scheme:
|
|
442
|
+
│ wss:// → WebSocketTransport (persistent, streaming chunks)
|
|
443
|
+
│ https:// → HttpStreamTransport (fetch ndjson)
|
|
444
|
+
│ "" → MockTransport (dev, simulate streaming)
|
|
445
|
+
▼
|
|
446
|
+
BFF → LLM Core → Business Services
|
|
447
|
+
(push TransportFrame chunks: {type:'chunk'} → {type:'done'})
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**UI States:**
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
bubble ──click──► mini ──intent: show_ui──► fullscreen (split 35/65)
|
|
454
|
+
▲ │ │
|
|
455
|
+
└──────close─────┘◄─────────close───────────────┘
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Scope POC
|
|
459
|
+
|
|
460
|
+
**In scope:** 3 UI states, pluggable transport (WS/HTTP streaming/mock), streaming responses, demo app, bidirectional communication, quick reply, mobile mode layout
|
|
461
|
+
|
|
462
|
+
**Out of scope:** BFF thật, authentication, message persistence
|