@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.
Files changed (40) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/README.md +276 -0
  3. package/dist/index.html +236 -0
  4. package/dist/test-local.html +268 -0
  5. package/dist/test-production.html +455 -0
  6. package/dist/widget.js +2781 -0
  7. package/package.json +41 -0
  8. package/scripts/build.js +73 -0
  9. package/scripts/dev.js +183 -0
  10. package/scripts/mock-server.js +436 -0
  11. package/src/components/BaseComponent.ts +67 -0
  12. package/src/components/ChatWindow.ts +283 -0
  13. package/src/components/ConnectionStatus.ts +50 -0
  14. package/src/components/InputArea.ts +164 -0
  15. package/src/components/Launcher.ts +121 -0
  16. package/src/components/MessageList.ts +150 -0
  17. package/src/components/QuickReplies.ts +42 -0
  18. package/src/components/icons.ts +51 -0
  19. package/src/components/index.ts +9 -0
  20. package/src/core/Widget.ts +478 -0
  21. package/src/core/index.ts +1 -0
  22. package/src/i18n/index.ts +85 -0
  23. package/src/i18n/translations.ts +150 -0
  24. package/src/index.ts +76 -0
  25. package/src/services/ChatHistoryManager.ts +98 -0
  26. package/src/services/StateManager.ts +148 -0
  27. package/src/services/WebSocketClient.ts +295 -0
  28. package/src/services/index.ts +3 -0
  29. package/src/styles/base.ts +231 -0
  30. package/src/styles/chatWindow.ts +239 -0
  31. package/src/styles/index.ts +21 -0
  32. package/src/styles/launcher.ts +186 -0
  33. package/src/styles/messages.ts +329 -0
  34. package/src/types/index.ts +345 -0
  35. package/src/utils/dom.ts +130 -0
  36. package/src/utils/events.ts +52 -0
  37. package/src/utils/id.ts +41 -0
  38. package/src/utils/index.ts +129 -0
  39. package/src/utils/storage.ts +64 -0
  40. 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.
@@ -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>