@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,268 @@
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 - Local 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: 300px;
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
+ .status {
58
+ display: inline-flex;
59
+ align-items: center;
60
+ gap: 6px;
61
+ padding: 6px 12px;
62
+ background: #f3f4f6;
63
+ border-radius: 20px;
64
+ font-size: 13px;
65
+ margin-bottom: 16px;
66
+ }
67
+ .status-dot {
68
+ width: 8px;
69
+ height: 8px;
70
+ border-radius: 50%;
71
+ background: #9CA3AF;
72
+ }
73
+ .status-dot.connected { background: #22C55E; }
74
+ .status-dot.connecting { background: #F59E0B; animation: pulse 1s infinite; }
75
+ .status-dot.failed { background: #EF4444; }
76
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
77
+ .info {
78
+ background: #EFF6FF;
79
+ border: 1px solid #BFDBFE;
80
+ border-radius: 8px;
81
+ padding: 16px;
82
+ margin-bottom: 16px;
83
+ color: #1E40AF;
84
+ font-size: 14px;
85
+ }
86
+ .info code {
87
+ background: #DBEAFE;
88
+ padding: 2px 6px;
89
+ border-radius: 4px;
90
+ font-family: monospace;
91
+ }
92
+ .settings {
93
+ display: flex;
94
+ align-items: center;
95
+ flex-wrap: wrap;
96
+ gap: 8px;
97
+ }
98
+ .settings label {
99
+ font-weight: 500;
100
+ color: #374151;
101
+ }
102
+ </style>
103
+ </head>
104
+ <body>
105
+ <div class="container">
106
+ <h1>Jidou Chat Widget</h1>
107
+ <p class="subtitle">Local ChatAgent Test (port 4000)</p>
108
+
109
+ <div class="card">
110
+ <h2>Connection Settings</h2>
111
+ <div class="settings">
112
+ <label for="client-id">Client ID:</label>
113
+ <input type="text" id="client-id" value="APTW_JIDOU_250101" style="width: 300px; padding: 8px; border: 1px solid #ccc; border-radius: 6px; font-size: 14px; margin-left: 8px;">
114
+ <button onclick="reconnect()" style="margin-left: 8px;">Reconnect</button>
115
+ </div>
116
+ <div class="status" style="margin-top: 16px;">
117
+ <span class="status-dot" id="status-dot"></span>
118
+ <span id="status-text">Disconnected</span>
119
+ </div>
120
+ <div class="info">
121
+ WebSocket Server: <code>ws://localhost:4000/ws</code>
122
+ </div>
123
+ </div>
124
+
125
+ <div class="card">
126
+ <h2>Widget Controls</h2>
127
+ <div class="controls">
128
+ <button onclick="JidouChat.open()">Open Chat</button>
129
+ <button onclick="JidouChat.close()">Close Chat</button>
130
+ <button onclick="JidouChat.toggle()">Toggle</button>
131
+ <button class="secondary" onclick="JidouChat.sendMessage('Hello!')">Send "Hello!"</button>
132
+ <button class="secondary" onclick="JidouChat.sendMessage('你好')">Send "你好"</button>
133
+ </div>
134
+ </div>
135
+
136
+ <div class="card">
137
+ <h2>Display Mode</h2>
138
+ <div class="controls">
139
+ <button onclick="switchMode('floating')">Floating</button>
140
+ <button onclick="switchMode('fullscreen')">Fullscreen</button>
141
+ <button onclick="switchMode('embedded')">Embedded</button>
142
+ </div>
143
+ <div id="embed-container" style="width: 100%; height: 500px; border: 2px dashed #ccc; border-radius: 12px; display: none; background: #f9fafb; margin-top: 16px;"></div>
144
+ </div>
145
+
146
+ <div class="card">
147
+ <h2>Event Log</h2>
148
+ <div class="log" id="log"></div>
149
+ </div>
150
+ </div>
151
+
152
+ <script>
153
+ function getClientId() {
154
+ return document.getElementById('client-id').value || 'APTW_JIDOU_250101';
155
+ }
156
+
157
+ window.JidouChatSettings = {
158
+ clientId: 'APTW_JIDOU_250101',
159
+ displayMode: 'floating',
160
+ theme: 'light',
161
+ wsUrl: 'ws://localhost:4000/ws',
162
+ colors: { primary: '#4F46E5' },
163
+ chatWindow: {
164
+ header: {
165
+ title: 'AI 助理',
166
+ subtitle: 'Local Test'
167
+ },
168
+ welcomeScreen: {
169
+ quickReplies: {
170
+ items: [
171
+ { text: '什麼是 Jidou AI?', icon: '🤖' },
172
+ { text: '價格方案', icon: '💰' },
173
+ { text: '聯絡我們', icon: '📧' },
174
+ ]
175
+ }
176
+ }
177
+ },
178
+ hooks: {
179
+ onLoad: function() { log('onLoad'); },
180
+ onReady: function() { log('onReady'); },
181
+ onOpen: function() { log('onOpen'); },
182
+ onClose: function() { log('onClose'); },
183
+ onMessageSent: function(msg) { log('onMessageSent', msg); },
184
+ onMessageReceived: function(msg) { log('onMessageReceived', msg); },
185
+ onConnectionStateChange: function(state) {
186
+ log('onConnectionStateChange', state);
187
+ updateStatus(state);
188
+ },
189
+ onReconnect: function(attempt) { log('onReconnect', 'Attempt ' + attempt); },
190
+ onError: function(err) { log('onError', err.message); }
191
+ }
192
+ };
193
+
194
+ function reconnect() {
195
+ var newClientId = getClientId();
196
+ log('reconnect', 'Destroying widget and reconnecting with clientId: ' + newClientId);
197
+
198
+ if (window.JidouChat) {
199
+ JidouChat.destroy();
200
+ }
201
+
202
+ window.JidouChatSettings.clientId = newClientId;
203
+
204
+ // Reinitialize widget
205
+ if (window.JidouWidget) {
206
+ window.JidouChat = new JidouWidget.Widget(window.JidouChatSettings);
207
+ window.JidouChat.init();
208
+ }
209
+ }
210
+
211
+ function log(event, data) {
212
+ var el = document.getElementById('log');
213
+ var entry = document.createElement('div');
214
+ entry.className = 'log-entry';
215
+
216
+ var time = document.createElement('span');
217
+ time.className = 'log-time';
218
+ time.textContent = new Date().toISOString().slice(11, 19) + ' ';
219
+ entry.appendChild(time);
220
+
221
+ var eventSpan = document.createElement('span');
222
+ eventSpan.className = 'log-event';
223
+ eventSpan.textContent = event;
224
+ entry.appendChild(eventSpan);
225
+
226
+ if (data !== undefined) {
227
+ var dataSpan = document.createElement('span');
228
+ dataSpan.className = 'log-data';
229
+ dataSpan.textContent = ' ' + (typeof data === 'object' ? JSON.stringify(data) : data);
230
+ entry.appendChild(dataSpan);
231
+ }
232
+
233
+ el.insertBefore(entry, el.firstChild);
234
+ }
235
+
236
+ function updateStatus(state) {
237
+ var dot = document.getElementById('status-dot');
238
+ var text = document.getElementById('status-text');
239
+ dot.className = 'status-dot';
240
+ if (state === 'connected') {
241
+ dot.classList.add('connected');
242
+ text.textContent = 'Connected';
243
+ } else if (state === 'connecting' || state === 'reconnecting') {
244
+ dot.classList.add('connecting');
245
+ text.textContent = state === 'reconnecting' ? 'Reconnecting...' : 'Connecting...';
246
+ } else if (state === 'failed') {
247
+ dot.classList.add('failed');
248
+ text.textContent = 'Connection Failed';
249
+ } else {
250
+ text.textContent = 'Disconnected';
251
+ }
252
+ }
253
+
254
+ function switchMode(mode) {
255
+ var container = document.getElementById('embed-container');
256
+ if (mode === 'embedded') {
257
+ container.style.display = 'block';
258
+ JidouChat.setDisplayMode('embedded', { container: '#embed-container' });
259
+ } else {
260
+ container.style.display = 'none';
261
+ JidouChat.setDisplayMode(mode);
262
+ }
263
+ log('switchMode', mode);
264
+ }
265
+ </script>
266
+ <script src="/widget.js"></script>
267
+ </body>
268
+ </html>