@jidou-ai/chat-widget 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/.claude/settings.local.json +28 -0
- package/README.md +276 -0
- package/dist/index.html +236 -0
- package/dist/test-local.html +268 -0
- package/dist/test-production.html +455 -0
- package/dist/widget.js +2781 -0
- package/package.json +41 -0
- package/scripts/build.js +73 -0
- package/scripts/dev.js +183 -0
- package/scripts/mock-server.js +436 -0
- package/src/components/BaseComponent.ts +67 -0
- package/src/components/ChatWindow.ts +283 -0
- package/src/components/ConnectionStatus.ts +50 -0
- package/src/components/InputArea.ts +164 -0
- package/src/components/Launcher.ts +121 -0
- package/src/components/MessageList.ts +150 -0
- package/src/components/QuickReplies.ts +42 -0
- package/src/components/icons.ts +51 -0
- package/src/components/index.ts +9 -0
- package/src/core/Widget.ts +478 -0
- package/src/core/index.ts +1 -0
- package/src/i18n/index.ts +85 -0
- package/src/i18n/translations.ts +150 -0
- package/src/index.ts +76 -0
- package/src/services/ChatHistoryManager.ts +98 -0
- package/src/services/StateManager.ts +148 -0
- package/src/services/WebSocketClient.ts +295 -0
- package/src/services/index.ts +3 -0
- package/src/styles/base.ts +231 -0
- package/src/styles/chatWindow.ts +239 -0
- package/src/styles/index.ts +21 -0
- package/src/styles/launcher.ts +186 -0
- package/src/styles/messages.ts +329 -0
- package/src/types/index.ts +345 -0
- package/src/utils/dom.ts +130 -0
- package/src/utils/events.ts +52 -0
- package/src/utils/id.ts +41 -0
- package/src/utils/index.ts +129 -0
- package/src/utils/storage.ts +64 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"mcp__heimin__heimin_task_get_details",
|
|
5
|
+
"mcp__heimin__heimin_page_get_details",
|
|
6
|
+
"Bash(npm install:*)",
|
|
7
|
+
"Bash(npm run typecheck:*)",
|
|
8
|
+
"Bash(npm run build:*)",
|
|
9
|
+
"Bash(gzip:*)",
|
|
10
|
+
"mcp__heimin__heimin_task_update",
|
|
11
|
+
"Bash(timeout 5 npm run dev:mock:*)",
|
|
12
|
+
"Bash(grep:*)",
|
|
13
|
+
"Bash(git init:*)",
|
|
14
|
+
"Bash(git remote add:*)",
|
|
15
|
+
"Bash(git -C /Users/shihyunshen/Projects/jidou/jidou-chat-widget status)",
|
|
16
|
+
"Bash(git -C /Users/shihyunshen/Projects/jidou/jidou-chat-widget log --oneline -5)",
|
|
17
|
+
"Bash(git -C /Users/shihyunshen/Projects/jidou/jidou-chat-widget add .gitignore README.md package.json package-lock.json tsconfig.json scripts/ src/)",
|
|
18
|
+
"Bash(git -C /Users/shihyunshen/Projects/jidou/jidou-chat-widget commit -m \"$\\(cat <<''EOF''\nInitial commit: Jidou Chat Widget\n\n- Lightweight, customizable chat widget for AI-powered customer support\n- Multiple display modes: floating, fullscreen, embedded\n- Internationalization: en, zh-TW, zh-CN, ja\n- Light/dark themes with customizable colors\n- WebSocket-based real-time messaging with auto-reconnect\n- Message persistence with local storage\n- Shadow DOM for style isolation\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
|
19
|
+
"Bash(git add:*)",
|
|
20
|
+
"Bash(git commit:*)",
|
|
21
|
+
"Bash(git push:*)",
|
|
22
|
+
"mcp__heimin__heimin_user_list_teams",
|
|
23
|
+
"mcp__heimin__heimin_team_list_projects",
|
|
24
|
+
"mcp__heimin__heimin_project_list_pages",
|
|
25
|
+
"mcp__heimin__heimin_project_list_tasks"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Jidou Chat Widget
|
|
2
|
+
|
|
3
|
+
A lightweight, customizable chat widget for AI-powered customer support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Display Modes**: Floating, fullscreen, and embedded
|
|
8
|
+
- **Internationalization**: Supports English, Traditional Chinese (zh-TW), Simplified Chinese (zh-CN), and Japanese
|
|
9
|
+
- **Theming**: Light/dark themes with customizable colors
|
|
10
|
+
- **Real-time Communication**: WebSocket-based messaging with auto-reconnect
|
|
11
|
+
- **Message Persistence**: Local storage for chat history
|
|
12
|
+
- **Responsive Design**: Works on desktop and mobile
|
|
13
|
+
- **Shadow DOM**: Isolated styles that won't conflict with host page
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Via Script Tag
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<script>
|
|
21
|
+
window.JidouChatSettings = {
|
|
22
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
23
|
+
// ... other options
|
|
24
|
+
};
|
|
25
|
+
</script>
|
|
26
|
+
<script src="https://cdn.jidou.ai/widget.js"></script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Via NPM
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @jidou-ai/chat-widget
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { Widget } from '@jidou-ai/chat-widget';
|
|
37
|
+
|
|
38
|
+
const widget = new Widget({
|
|
39
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
40
|
+
// ... other options
|
|
41
|
+
});
|
|
42
|
+
widget.init();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
window.JidouChatSettings = {
|
|
49
|
+
// Required
|
|
50
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
51
|
+
|
|
52
|
+
// Display mode: 'floating' | 'fullscreen' | 'embedded'
|
|
53
|
+
displayMode: 'floating',
|
|
54
|
+
|
|
55
|
+
// Language: 'en' | 'zh-TW' | 'zh-CN' | 'ja' | 'auto'
|
|
56
|
+
language: 'auto',
|
|
57
|
+
|
|
58
|
+
// Theme: 'light' | 'dark'
|
|
59
|
+
theme: 'light',
|
|
60
|
+
|
|
61
|
+
// WebSocket URL (optional, uses default if not specified)
|
|
62
|
+
wsUrl: 'wss://chat.jidou.ai/ws',
|
|
63
|
+
|
|
64
|
+
// Custom colors
|
|
65
|
+
colors: {
|
|
66
|
+
primary: '#4F46E5',
|
|
67
|
+
background: '#FFFFFF',
|
|
68
|
+
text: '#1F2937',
|
|
69
|
+
botBubble: '#F3F4F6',
|
|
70
|
+
userBubble: '#4F46E5',
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Launcher configuration (floating mode only)
|
|
74
|
+
launcher: {
|
|
75
|
+
show: true,
|
|
76
|
+
position: 'bottom-right', // 'bottom-right' | 'bottom-left'
|
|
77
|
+
size: 'medium', // 'small' | 'medium' | 'large'
|
|
78
|
+
shape: 'circle', // 'circle' | 'rounded'
|
|
79
|
+
offsetX: 20,
|
|
80
|
+
offsetY: 20,
|
|
81
|
+
tooltip: {
|
|
82
|
+
show: true,
|
|
83
|
+
text: 'Need help?',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// Chat window configuration
|
|
88
|
+
chatWindow: {
|
|
89
|
+
width: 380,
|
|
90
|
+
height: 600,
|
|
91
|
+
borderRadius: 16,
|
|
92
|
+
header: {
|
|
93
|
+
title: 'AI Assistant',
|
|
94
|
+
subtitle: 'Online',
|
|
95
|
+
logo: {
|
|
96
|
+
show: true,
|
|
97
|
+
url: 'https://example.com/logo.png',
|
|
98
|
+
size: 40,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
footer: {
|
|
102
|
+
input: {
|
|
103
|
+
placeholder: 'Type a message...',
|
|
104
|
+
maxLength: 1000,
|
|
105
|
+
},
|
|
106
|
+
branding: {
|
|
107
|
+
show: true,
|
|
108
|
+
text: 'Powered by Jidou',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
welcomeScreen: {
|
|
112
|
+
quickReplies: {
|
|
113
|
+
items: [
|
|
114
|
+
{ text: 'Getting Started', icon: '🚀' },
|
|
115
|
+
{ text: 'Pricing', icon: '💰' },
|
|
116
|
+
{ text: 'Support', icon: '🆘' },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Behavior configuration
|
|
123
|
+
behavior: {
|
|
124
|
+
autoOpen: {
|
|
125
|
+
enabled: false,
|
|
126
|
+
delay: 3000,
|
|
127
|
+
},
|
|
128
|
+
persistence: {
|
|
129
|
+
enabled: true,
|
|
130
|
+
duration: 7, // days
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// Container for embedded mode
|
|
135
|
+
container: '#chat-container',
|
|
136
|
+
|
|
137
|
+
// Z-index
|
|
138
|
+
zIndex: 9999,
|
|
139
|
+
|
|
140
|
+
// Event hooks
|
|
141
|
+
hooks: {
|
|
142
|
+
onLoad: () => {},
|
|
143
|
+
onReady: () => {},
|
|
144
|
+
onOpen: () => {},
|
|
145
|
+
onClose: () => {},
|
|
146
|
+
onMessageSent: (message) => {},
|
|
147
|
+
onMessageReceived: (message) => {},
|
|
148
|
+
onConnectionStateChange: (state) => {},
|
|
149
|
+
onReconnect: (attempt) => {},
|
|
150
|
+
onError: (error) => {},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## API
|
|
156
|
+
|
|
157
|
+
The widget exposes a global `JidouChat` object with the following methods:
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// Open the chat window
|
|
161
|
+
JidouChat.open();
|
|
162
|
+
|
|
163
|
+
// Close the chat window
|
|
164
|
+
JidouChat.close();
|
|
165
|
+
|
|
166
|
+
// Toggle the chat window
|
|
167
|
+
JidouChat.toggle();
|
|
168
|
+
|
|
169
|
+
// Check if chat is open
|
|
170
|
+
JidouChat.isOpen();
|
|
171
|
+
|
|
172
|
+
// Send a message programmatically
|
|
173
|
+
JidouChat.sendMessage('Hello!');
|
|
174
|
+
|
|
175
|
+
// Change display mode
|
|
176
|
+
JidouChat.setDisplayMode('fullscreen');
|
|
177
|
+
JidouChat.setDisplayMode('floating');
|
|
178
|
+
JidouChat.setDisplayMode('embedded', { container: '#my-container' });
|
|
179
|
+
|
|
180
|
+
// Get current display mode
|
|
181
|
+
JidouChat.getDisplayMode();
|
|
182
|
+
|
|
183
|
+
// Get current state
|
|
184
|
+
JidouChat.getState();
|
|
185
|
+
|
|
186
|
+
// Subscribe to events
|
|
187
|
+
JidouChat.on('onMessageReceived', (message) => {
|
|
188
|
+
console.log('New message:', message);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Unsubscribe from events
|
|
192
|
+
JidouChat.off('onMessageReceived', handler);
|
|
193
|
+
|
|
194
|
+
// Destroy the widget
|
|
195
|
+
JidouChat.destroy();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Internationalization
|
|
199
|
+
|
|
200
|
+
The widget automatically detects browser language when `language: 'auto'` is set. Supported languages:
|
|
201
|
+
|
|
202
|
+
| Code | Language |
|
|
203
|
+
|------|----------|
|
|
204
|
+
| `en` | English |
|
|
205
|
+
| `zh-TW` | Traditional Chinese |
|
|
206
|
+
| `zh-CN` | Simplified Chinese |
|
|
207
|
+
| `ja` | Japanese |
|
|
208
|
+
|
|
209
|
+
Translated strings include:
|
|
210
|
+
- Connection status (Connecting, Connected, Disconnected, etc.)
|
|
211
|
+
- Chat window title
|
|
212
|
+
- Input placeholder
|
|
213
|
+
- Button labels (Close, Expand, Collapse)
|
|
214
|
+
- Branding text
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
### Prerequisites
|
|
219
|
+
|
|
220
|
+
- Node.js 18+
|
|
221
|
+
- npm or yarn
|
|
222
|
+
|
|
223
|
+
### Setup
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Install dependencies
|
|
227
|
+
npm install
|
|
228
|
+
|
|
229
|
+
# Start development server with mock WebSocket
|
|
230
|
+
npm run dev
|
|
231
|
+
|
|
232
|
+
# Build for production
|
|
233
|
+
npm run build
|
|
234
|
+
|
|
235
|
+
# Run tests
|
|
236
|
+
npm test
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Project Structure
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
src/
|
|
243
|
+
├── components/ # UI components
|
|
244
|
+
│ ├── BaseComponent.ts
|
|
245
|
+
│ ├── ChatWindow.ts
|
|
246
|
+
│ ├── ConnectionStatus.ts
|
|
247
|
+
│ ├── InputArea.ts
|
|
248
|
+
│ ├── Launcher.ts
|
|
249
|
+
│ ├── MessageList.ts
|
|
250
|
+
│ ├── QuickReplies.ts
|
|
251
|
+
│ └── icons.ts
|
|
252
|
+
├── core/
|
|
253
|
+
│ └── Widget.ts # Main widget class
|
|
254
|
+
├── i18n/ # Internationalization
|
|
255
|
+
│ ├── index.ts
|
|
256
|
+
│ └── translations.ts
|
|
257
|
+
├── services/ # Business logic
|
|
258
|
+
│ ├── ChatHistoryManager.ts
|
|
259
|
+
│ ├── StateManager.ts
|
|
260
|
+
│ └── WebSocketClient.ts
|
|
261
|
+
├── styles/ # CSS styles
|
|
262
|
+
├── types/ # TypeScript types
|
|
263
|
+
├── utils/ # Utility functions
|
|
264
|
+
└── index.ts # Entry point
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Browser Support
|
|
268
|
+
|
|
269
|
+
- Chrome (latest)
|
|
270
|
+
- Firefox (latest)
|
|
271
|
+
- Safari (latest)
|
|
272
|
+
- Edge (latest)
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
Copyright © Jidou AI. All rights reserved.
|
package/dist/index.html
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-TW">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Jidou Chat Widget - Test</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 40px;
|
|
13
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
14
|
+
min-height: 100vh;
|
|
15
|
+
}
|
|
16
|
+
.container { max-width: 800px; margin: 0 auto; }
|
|
17
|
+
h1 { color: white; margin-bottom: 8px; }
|
|
18
|
+
.subtitle { color: rgba(255,255,255,0.8); margin-bottom: 32px; }
|
|
19
|
+
.card {
|
|
20
|
+
background: white;
|
|
21
|
+
border-radius: 12px;
|
|
22
|
+
padding: 24px;
|
|
23
|
+
margin-bottom: 24px;
|
|
24
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
25
|
+
}
|
|
26
|
+
h2 { margin-top: 0; color: #333; font-size: 18px; }
|
|
27
|
+
.controls { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
28
|
+
button {
|
|
29
|
+
padding: 10px 20px;
|
|
30
|
+
border: none;
|
|
31
|
+
border-radius: 8px;
|
|
32
|
+
background: #4F46E5;
|
|
33
|
+
color: white;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
transition: all 0.2s;
|
|
38
|
+
}
|
|
39
|
+
button:hover { background: #4338CA; transform: translateY(-1px); }
|
|
40
|
+
button.secondary { background: #E5E7EB; color: #374151; }
|
|
41
|
+
button.secondary:hover { background: #D1D5DB; }
|
|
42
|
+
.log {
|
|
43
|
+
margin-top: 16px;
|
|
44
|
+
padding: 16px;
|
|
45
|
+
background: #1a1a2e;
|
|
46
|
+
color: #0f0;
|
|
47
|
+
font-family: 'Monaco', 'Consolas', monospace;
|
|
48
|
+
font-size: 12px;
|
|
49
|
+
border-radius: 8px;
|
|
50
|
+
max-height: 200px;
|
|
51
|
+
overflow-y: auto;
|
|
52
|
+
}
|
|
53
|
+
.log-entry { margin: 4px 0; padding: 4px 0; border-bottom: 1px solid rgba(255,255,255,0.1); }
|
|
54
|
+
.log-time { color: #888; }
|
|
55
|
+
.log-event { color: #4FC3F7; }
|
|
56
|
+
.log-data { color: #81C784; }
|
|
57
|
+
#embed-container {
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 500px;
|
|
60
|
+
border: 2px dashed #ccc;
|
|
61
|
+
border-radius: 12px;
|
|
62
|
+
display: none;
|
|
63
|
+
background: #f9fafb;
|
|
64
|
+
margin-top: 16px;
|
|
65
|
+
}
|
|
66
|
+
.status {
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 6px;
|
|
70
|
+
padding: 6px 12px;
|
|
71
|
+
background: #f3f4f6;
|
|
72
|
+
border-radius: 20px;
|
|
73
|
+
font-size: 13px;
|
|
74
|
+
margin-bottom: 16px;
|
|
75
|
+
}
|
|
76
|
+
.status-dot {
|
|
77
|
+
width: 8px;
|
|
78
|
+
height: 8px;
|
|
79
|
+
border-radius: 50%;
|
|
80
|
+
background: #9CA3AF;
|
|
81
|
+
}
|
|
82
|
+
.status-dot.connected { background: #22C55E; }
|
|
83
|
+
.status-dot.connecting { background: #F59E0B; animation: pulse 1s infinite; }
|
|
84
|
+
.status-dot.failed { background: #EF4444; }
|
|
85
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
86
|
+
.info {
|
|
87
|
+
background: #EFF6FF;
|
|
88
|
+
border: 1px solid #BFDBFE;
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
padding: 16px;
|
|
91
|
+
margin-bottom: 16px;
|
|
92
|
+
color: #1E40AF;
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<div class="container">
|
|
99
|
+
<h1>Jidou Chat Widget</h1>
|
|
100
|
+
<p class="subtitle">Test Environment with Mock Server</p>
|
|
101
|
+
|
|
102
|
+
<div class="card">
|
|
103
|
+
<h2>Connection Status</h2>
|
|
104
|
+
<div class="status">
|
|
105
|
+
<span class="status-dot" id="status-dot"></span>
|
|
106
|
+
<span id="status-text">Disconnected</span>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="info">
|
|
109
|
+
Mock WebSocket Server: ws://localhost:8080<br>
|
|
110
|
+
HTTP Server: http://localhost:3000
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div class="card">
|
|
115
|
+
<h2>Widget Controls</h2>
|
|
116
|
+
<div class="controls">
|
|
117
|
+
<button onclick="JidouChat.open()">Open Chat</button>
|
|
118
|
+
<button onclick="JidouChat.close()">Close Chat</button>
|
|
119
|
+
<button onclick="JidouChat.toggle()">Toggle</button>
|
|
120
|
+
<button class="secondary" onclick="JidouChat.sendMessage('Hello!')">Send "Hello!"</button>
|
|
121
|
+
<button class="secondary" onclick="JidouChat.sendMessage('價格是多少?')">Ask Price</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div class="card">
|
|
126
|
+
<h2>Display Mode</h2>
|
|
127
|
+
<div class="controls">
|
|
128
|
+
<button onclick="switchMode('floating')">Floating</button>
|
|
129
|
+
<button onclick="switchMode('fullscreen')">Fullscreen</button>
|
|
130
|
+
<button onclick="switchMode('embedded')">Embedded</button>
|
|
131
|
+
</div>
|
|
132
|
+
<div id="embed-container"></div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="card">
|
|
136
|
+
<h2>Event Log</h2>
|
|
137
|
+
<div class="log" id="log"></div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<script src="/widget.js"></script>
|
|
142
|
+
<script>
|
|
143
|
+
function log(event, data) {
|
|
144
|
+
var el = document.getElementById('log');
|
|
145
|
+
var entry = document.createElement('div');
|
|
146
|
+
entry.className = 'log-entry';
|
|
147
|
+
|
|
148
|
+
var time = document.createElement('span');
|
|
149
|
+
time.className = 'log-time';
|
|
150
|
+
time.textContent = new Date().toISOString().slice(11, 19) + ' ';
|
|
151
|
+
entry.appendChild(time);
|
|
152
|
+
|
|
153
|
+
var eventSpan = document.createElement('span');
|
|
154
|
+
eventSpan.className = 'log-event';
|
|
155
|
+
eventSpan.textContent = event;
|
|
156
|
+
entry.appendChild(eventSpan);
|
|
157
|
+
|
|
158
|
+
if (data !== undefined) {
|
|
159
|
+
var dataSpan = document.createElement('span');
|
|
160
|
+
dataSpan.className = 'log-data';
|
|
161
|
+
dataSpan.textContent = ' ' + (typeof data === 'object' ? JSON.stringify(data) : data);
|
|
162
|
+
entry.appendChild(dataSpan);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
el.insertBefore(entry, el.firstChild);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function updateStatus(state) {
|
|
169
|
+
var dot = document.getElementById('status-dot');
|
|
170
|
+
var text = document.getElementById('status-text');
|
|
171
|
+
dot.className = 'status-dot';
|
|
172
|
+
if (state === 'connected') {
|
|
173
|
+
dot.classList.add('connected');
|
|
174
|
+
text.textContent = 'Connected';
|
|
175
|
+
} else if (state === 'connecting' || state === 'reconnecting') {
|
|
176
|
+
dot.classList.add('connecting');
|
|
177
|
+
text.textContent = state === 'reconnecting' ? 'Reconnecting...' : 'Connecting...';
|
|
178
|
+
} else if (state === 'failed') {
|
|
179
|
+
dot.classList.add('failed');
|
|
180
|
+
text.textContent = 'Connection Failed';
|
|
181
|
+
} else {
|
|
182
|
+
text.textContent = 'Disconnected';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
window.JidouChatSettings = {
|
|
187
|
+
clientId: 'test-client',
|
|
188
|
+
displayMode: 'floating',
|
|
189
|
+
theme: 'light',
|
|
190
|
+
wsUrl: 'ws://localhost:8080',
|
|
191
|
+
colors: { primary: '#4F46E5' },
|
|
192
|
+
chatWindow: {
|
|
193
|
+
header: {
|
|
194
|
+
title: 'AI 助理',
|
|
195
|
+
subtitle: 'Online'
|
|
196
|
+
},
|
|
197
|
+
welcomeScreen: {
|
|
198
|
+
quickReplies: {
|
|
199
|
+
items: [
|
|
200
|
+
{ text: '什麼是 Jidou AI?', icon: '🤖' },
|
|
201
|
+
{ text: '價格方案', icon: '💰' },
|
|
202
|
+
{ text: '聯絡我們', icon: '📧' },
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
hooks: {
|
|
208
|
+
onLoad: function() { log('onLoad'); },
|
|
209
|
+
onReady: function() { log('onReady'); },
|
|
210
|
+
onOpen: function() { log('onOpen'); },
|
|
211
|
+
onClose: function() { log('onClose'); },
|
|
212
|
+
onMessageSent: function(msg) { log('onMessageSent', msg); },
|
|
213
|
+
onMessageReceived: function(msg) { log('onMessageReceived', msg); },
|
|
214
|
+
onConnectionStateChange: function(state) {
|
|
215
|
+
log('onConnectionStateChange', state);
|
|
216
|
+
updateStatus(state);
|
|
217
|
+
},
|
|
218
|
+
onReconnect: function(attempt) { log('onReconnect', 'Attempt ' + attempt); },
|
|
219
|
+
onError: function(err) { log('onError', err.message); }
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
function switchMode(mode) {
|
|
224
|
+
var container = document.getElementById('embed-container');
|
|
225
|
+
if (mode === 'embedded') {
|
|
226
|
+
container.style.display = 'block';
|
|
227
|
+
JidouChat.setDisplayMode('embedded', { container: '#embed-container' });
|
|
228
|
+
} else {
|
|
229
|
+
container.style.display = 'none';
|
|
230
|
+
JidouChat.setDisplayMode(mode);
|
|
231
|
+
}
|
|
232
|
+
log('switchMode', mode);
|
|
233
|
+
}
|
|
234
|
+
</script>
|
|
235
|
+
</body>
|
|
236
|
+
</html>
|