ttp-agent-sdk 2.34.10 → 2.34.13

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.
@@ -301,6 +301,21 @@
301
301
  <span class="badge" style="background: #fef3c7; color: #92400e;">Debug</span>
302
302
  </div>
303
303
  </a>
304
+
305
+ <a href="../examples/test-ecommerce.html" class="test-card">
306
+ <span class="test-card__icon">🛒</span>
307
+ <h3 class="test-card__title">E-Commerce Widget Test</h3>
308
+ <p class="test-card__description">
309
+ Test the TTPEcommerceWidget — a composition wrapper around TTPChatWidget
310
+ that adds product display, cart management, and partner integration.
311
+ Simulate show_products and cart_updated messages without a live voice call.
312
+ </p>
313
+ <div class="test-card__badges">
314
+ <span class="badge badge--widget">Widget</span>
315
+ <span class="badge badge--voice">Voice</span>
316
+ <span class="badge badge--new">E-Commerce</span>
317
+ </div>
318
+ </a>
304
319
  </div>
305
320
  </div>
306
321
 
@@ -0,0 +1,436 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>TTP E-Commerce Widget - Test</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
10
+ max-width: 1200px;
11
+ margin: 40px auto;
12
+ padding: 20px;
13
+ background: #F9FAFB;
14
+ }
15
+
16
+ .container {
17
+ background: white;
18
+ padding: 30px;
19
+ border-radius: 12px;
20
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
21
+ }
22
+
23
+ h1 { color: #111827; margin-top: 0; }
24
+ h2 { color: #374151; border-bottom: 2px solid #E5E7EB; padding-bottom: 8px; margin-top: 30px; }
25
+
26
+ .info {
27
+ background: #EFF6FF;
28
+ border-left: 4px solid #3B82F6;
29
+ padding: 16px;
30
+ margin: 20px 0;
31
+ border-radius: 4px;
32
+ }
33
+
34
+ .status {
35
+ margin: 20px 0;
36
+ padding: 12px;
37
+ background: #F3F4F6;
38
+ border-radius: 6px;
39
+ font-family: monospace;
40
+ font-size: 14px;
41
+ }
42
+ .status.success { background: #D1FAE5; color: #065F46; }
43
+ .status.error { background: #FEE2E2; color: #991B1B; }
44
+
45
+ button {
46
+ background: #4F46E5;
47
+ color: white;
48
+ border: none;
49
+ padding: 12px 24px;
50
+ border-radius: 6px;
51
+ cursor: pointer;
52
+ font-size: 16px;
53
+ margin: 10px 10px 10px 0;
54
+ }
55
+ button:hover { background: #4338CA; }
56
+ button.secondary { background: #6B7280; }
57
+ button.secondary:hover { background: #4B5563; }
58
+ button.success { background: #059669; }
59
+ button.success:hover { background: #047857; }
60
+ button.warning { background: #D97706; }
61
+ button.warning:hover { background: #B45309; }
62
+
63
+ label {
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: 4px;
67
+ font-size: 14px;
68
+ color: #374151;
69
+ }
70
+
71
+ select, input[type="text"], input[type="number"] {
72
+ padding: 8px 12px;
73
+ border: 1px solid #D1D5DB;
74
+ border-radius: 6px;
75
+ font-size: 14px;
76
+ font-family: inherit;
77
+ background: white;
78
+ color: #111827;
79
+ }
80
+
81
+ .code-block {
82
+ background: #1F2937;
83
+ color: #F9FAFB;
84
+ padding: 16px;
85
+ border-radius: 6px;
86
+ font-family: monospace;
87
+ font-size: 12px;
88
+ overflow-x: auto;
89
+ margin: 20px 0;
90
+ white-space: pre-wrap;
91
+ }
92
+
93
+ .settings-grid {
94
+ display: grid;
95
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
96
+ gap: 16px;
97
+ margin: 16px 0;
98
+ }
99
+
100
+ .test-section {
101
+ border: 1px solid #E5E7EB;
102
+ border-radius: 8px;
103
+ padding: 20px;
104
+ margin: 16px 0;
105
+ background: #F9FAFB;
106
+ }
107
+
108
+ .test-section h3 {
109
+ margin: 0 0 12px 0;
110
+ color: #111827;
111
+ }
112
+
113
+ .cart-display {
114
+ background: #F0FDF4;
115
+ border: 1px solid #BBF7D0;
116
+ border-radius: 8px;
117
+ padding: 16px;
118
+ margin: 16px 0;
119
+ font-family: monospace;
120
+ font-size: 13px;
121
+ }
122
+
123
+ .cart-display pre {
124
+ margin: 0;
125
+ white-space: pre-wrap;
126
+ }
127
+
128
+ @media (max-width: 768px) {
129
+ body { margin: 0; padding: 10px; }
130
+ .container { padding: 16px; border-radius: 8px; }
131
+ h1 { font-size: 24px; }
132
+ button { width: 100%; margin: 8px 0; }
133
+ .settings-grid { grid-template-columns: 1fr; }
134
+ }
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <div class="container">
139
+ <h1>🛒 TTP E-Commerce Widget Test</h1>
140
+
141
+ <div class="info">
142
+ <strong>What is TTPEcommerceWidget?</strong>
143
+ <ul>
144
+ <li>Wraps the standard TTPChatWidget with an e-commerce layer</li>
145
+ <li>Displays product cards when the agent sends <code>show_products</code> messages</li>
146
+ <li>Manages a local cart with toast notifications and cart summary</li>
147
+ <li>Supports partner integration via <code>addToCartFn</code> / <code>getCartFn</code> callbacks</li>
148
+ <li>Test API available at <code>window.__TTP_ECOMMERCE__</code></li>
149
+ </ul>
150
+ </div>
151
+
152
+ <div id="status" class="status">Loading SDK...</div>
153
+
154
+ <h2>Widget Configuration</h2>
155
+ <div class="settings-grid">
156
+ <label>Agent
157
+ <select id="agentSelect" onchange="handleAgentChange()">
158
+ <option value="agent_87c4a55a1|app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC|en|ltr">e-commerce_english (agent_87c4a55a1)</option>
159
+ <option value="agent_ed18369b3|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|he|rtl">e-commerce_hebrew (agent_ed18369b3)</option>
160
+ <option value="agent_d119003d8|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|es|ltr">ecommerce_spanish (agent_d119003d8)</option>
161
+ <option value="custom">Custom...</option>
162
+ </select>
163
+ </label>
164
+ <label id="customAgentLabel" style="display:none">Agent ID
165
+ <input id="customAgentId" type="text" placeholder="agent_..." />
166
+ </label>
167
+ <label id="customAppLabel" style="display:none">App ID
168
+ <input id="customAppId" type="text" placeholder="app_..." />
169
+ </label>
170
+ <label>Language
171
+ <select id="languageSelect">
172
+ <option value="en" selected>English</option>
173
+ <option value="he">Hebrew</option>
174
+ </select>
175
+ </label>
176
+ <label>Direction
177
+ <select id="dirSelect">
178
+ <option value="ltr" selected>LTR</option>
179
+ <option value="rtl">RTL</option>
180
+ </select>
181
+ </label>
182
+ <label>Widget Mode
183
+ <select id="modeSelect">
184
+ <option value="unified" selected>Unified (Both)</option>
185
+ <option value="voice-only">Voice Only</option>
186
+ <option value="text-only">Text Only</option>
187
+ </select>
188
+ </label>
189
+ <label>Start Open
190
+ <select id="startOpenSelect">
191
+ <option value="true" selected>Yes</option>
192
+ <option value="false">No</option>
193
+ </select>
194
+ </label>
195
+ </div>
196
+
197
+ <div style="margin-top: 16px;">
198
+ <button id="createBtn">Create Widget</button>
199
+ <button id="destroyBtn" class="secondary">Destroy Widget</button>
200
+ </div>
201
+
202
+ <h2>E-Commerce Test Actions</h2>
203
+
204
+ <div class="test-section">
205
+ <h3>Show Products (simulated)</h3>
206
+ <p style="color: #6B7280; font-size: 14px; margin-bottom: 12px;">
207
+ Injects a <code>show_products</code> message into the ecommerce layer — no voice call needed.
208
+ </p>
209
+ <div class="settings-grid">
210
+ <label>Search Query
211
+ <input id="productQuery" type="text" value="milk" />
212
+ </label>
213
+ <label>Layout
214
+ <select id="productLayout">
215
+ <option value="auto">Auto (cards &le;4, list 5+)</option>
216
+ <option value="cards">Cards</option>
217
+ <option value="list">List</option>
218
+ </select>
219
+ </label>
220
+ </div>
221
+ <button id="showProductsBtn" class="success">Show Products</button>
222
+ </div>
223
+
224
+ <div class="test-section">
225
+ <h3>Cart Updated (simulated)</h3>
226
+ <p style="color: #6B7280; font-size: 14px; margin-bottom: 12px;">
227
+ Injects a <code>cart_updated</code> message to test toast and summary UI.
228
+ </p>
229
+ <button id="cartAddBtn" class="success">Simulate: Product Added</button>
230
+ <button id="cartClearBtn" class="secondary">Clear Cart</button>
231
+ </div>
232
+
233
+ <div class="test-section">
234
+ <h3>Cart State</h3>
235
+ <div id="cartDisplay" class="cart-display">
236
+ <pre>No widget created yet</pre>
237
+ </div>
238
+ <button id="refreshCartBtn" class="secondary">Refresh Cart State</button>
239
+ </div>
240
+
241
+ <h2>Console Commands</h2>
242
+ <div class="code-block">// Show products via test API (fetches from mock API)
243
+ window.__TTP_ECOMMERCE__.showProducts('milk');
244
+ window.__TTP_ECOMMERCE__.showProducts('bread', 3);
245
+
246
+ // Inject raw messages
247
+ window.__TTP_ECOMMERCE__.handleMessage({
248
+ t: 'show_products',
249
+ products: [
250
+ { id: '1', name: 'Test Product', price: 9.99, image: '' }
251
+ ],
252
+ title: 'Test Results'
253
+ });
254
+
255
+ // Cart operations
256
+ window.__TTP_ECOMMERCE__.getCart();
257
+ window.__TTP_ECOMMERCE__.clearCart();</div>
258
+ </div>
259
+
260
+ <script src="/dist/agent-widget.js"></script>
261
+
262
+ <script>
263
+ let ecomWidget = null;
264
+
265
+ function updateStatus(msg, type = '') {
266
+ const el = document.getElementById('status');
267
+ el.textContent = msg;
268
+ el.className = 'status ' + type;
269
+ }
270
+
271
+ function refreshCartDisplay() {
272
+ const el = document.getElementById('cartDisplay');
273
+ if (!window.__TTP_ECOMMERCE__) {
274
+ el.innerHTML = '<pre>No widget created yet</pre>';
275
+ return;
276
+ }
277
+ const cart = window.__TTP_ECOMMERCE__.getCart();
278
+ el.innerHTML = '<pre>' + JSON.stringify(cart, null, 2) + '</pre>';
279
+ }
280
+
281
+ function handleAgentChange() {
282
+ const sel = document.getElementById('agentSelect');
283
+ const isCustom = sel.value === 'custom';
284
+ document.getElementById('customAgentLabel').style.display = isCustom ? '' : 'none';
285
+ document.getElementById('customAppLabel').style.display = isCustom ? '' : 'none';
286
+
287
+ if (!isCustom) {
288
+ const [, , lang, dir] = sel.value.split('|');
289
+ document.getElementById('languageSelect').value = lang;
290
+ document.getElementById('dirSelect').value = dir;
291
+ }
292
+ }
293
+
294
+ function getAgentConfig() {
295
+ const sel = document.getElementById('agentSelect');
296
+ if (sel.value === 'custom') {
297
+ return {
298
+ agentId: document.getElementById('customAgentId').value,
299
+ appId: document.getElementById('customAppId').value
300
+ };
301
+ }
302
+ const [agentId, appId] = sel.value.split('|');
303
+ return { agentId, appId };
304
+ }
305
+
306
+ function createWidget() {
307
+ if (ecomWidget) {
308
+ ecomWidget.destroy();
309
+ ecomWidget = null;
310
+ }
311
+
312
+ const TTPAgentSDK = window.TTPAgentSDK;
313
+ if (!TTPAgentSDK || !TTPAgentSDK.TTPEcommerceWidget) {
314
+ updateStatus('TTPEcommerceWidget not available in SDK', 'error');
315
+ return;
316
+ }
317
+
318
+ const agent = getAgentConfig();
319
+
320
+ ecomWidget = new TTPAgentSDK.TTPEcommerceWidget({
321
+ agentId: agent.agentId,
322
+ appId: agent.appId,
323
+ language: document.getElementById('languageSelect').value,
324
+ direction: document.getElementById('dirSelect').value,
325
+ behavior: {
326
+ mode: document.getElementById('modeSelect').value,
327
+ startOpen: document.getElementById('startOpenSelect').value === 'true'
328
+ },
329
+ icon: {
330
+ type: 'custom',
331
+ customImage: 'https://talktopc.com/logo192.png',
332
+ size: 'medium',
333
+ backgroundColor: '#FFFFFF'
334
+ },
335
+ partner: { name: 'TestStore' },
336
+ cart: {
337
+ addToCartFn: async (productId, qty) => {
338
+ console.log('[TestPage] addToCartFn called:', productId, qty);
339
+ return { ok: true };
340
+ },
341
+ getCartFn: async () => {
342
+ console.log('[TestPage] getCartFn called');
343
+ return window.__TTP_ECOMMERCE__?.getCart() || { items: [], total: 0, count: 0 };
344
+ }
345
+ }
346
+ });
347
+
348
+ window.testWidget = ecomWidget;
349
+ updateStatus('TTPEcommerceWidget created successfully', 'success');
350
+ refreshCartDisplay();
351
+ }
352
+
353
+ // Button handlers
354
+ document.getElementById('createBtn').onclick = createWidget;
355
+
356
+ document.getElementById('destroyBtn').onclick = () => {
357
+ if (ecomWidget) {
358
+ ecomWidget.destroy();
359
+ ecomWidget = null;
360
+ updateStatus('Widget destroyed', '');
361
+ refreshCartDisplay();
362
+ }
363
+ };
364
+
365
+ document.getElementById('showProductsBtn').onclick = async () => {
366
+ if (!window.__TTP_ECOMMERCE__) {
367
+ updateStatus('Create widget first', 'error');
368
+ return;
369
+ }
370
+ const query = document.getElementById('productQuery').value || 'milk';
371
+ try {
372
+ await window.__TTP_ECOMMERCE__.showProducts(query);
373
+ updateStatus('showProducts("' + query + '") sent', 'success');
374
+ } catch (e) {
375
+ updateStatus('showProducts failed: ' + e.message, 'error');
376
+ }
377
+ };
378
+
379
+ document.getElementById('cartAddBtn').onclick = async () => {
380
+ if (!window.__TTP_ECOMMERCE__) {
381
+ updateStatus('Create widget first', 'error');
382
+ return;
383
+ }
384
+ try {
385
+ const baseUrl = window.__TTP_MOCK_API__ || 'https://backend.talktopc.com';
386
+ const response = await fetch(`${baseUrl}/api/partner/mock-store/products/search?q=milk&limit=1&sessionId=test`);
387
+ const data = await response.json();
388
+ const product = data.products[0];
389
+ window.__TTP_ECOMMERCE__.handleMessage({
390
+ t: 'cart_updated',
391
+ product: product,
392
+ action: 'added',
393
+ cartTotal: product.price,
394
+ cartItemCount: 1
395
+ });
396
+ updateStatus('Simulated cart_updated for "' + product.name + '"', 'success');
397
+ setTimeout(refreshCartDisplay, 200);
398
+ } catch (e) {
399
+ updateStatus('Failed to fetch product: ' + e.message, 'error');
400
+ }
401
+ };
402
+
403
+ document.getElementById('cartClearBtn').onclick = () => {
404
+ if (!window.__TTP_ECOMMERCE__) {
405
+ updateStatus('Create widget first', 'error');
406
+ return;
407
+ }
408
+ window.__TTP_ECOMMERCE__.clearCart();
409
+ updateStatus('Cart cleared', '');
410
+ refreshCartDisplay();
411
+ };
412
+
413
+ document.getElementById('refreshCartBtn').onclick = refreshCartDisplay;
414
+
415
+ // Wait for SDK and auto-create widget
416
+ function waitForSDK() {
417
+ return new Promise((resolve, reject) => {
418
+ if (window.TTPAgentSDK?.TTPEcommerceWidget) { resolve(); return; }
419
+ let attempts = 0;
420
+ const iv = setInterval(() => {
421
+ attempts++;
422
+ if (window.TTPAgentSDK?.TTPEcommerceWidget) { clearInterval(iv); resolve(); }
423
+ else if (attempts >= 50) { clearInterval(iv); reject(new Error('SDK failed to load')); }
424
+ }, 100);
425
+ });
426
+ }
427
+
428
+ waitForSDK().then(() => {
429
+ updateStatus('SDK loaded — creating widget...', '');
430
+ createWidget();
431
+ }).catch(e => {
432
+ updateStatus('SDK load failed: ' + e.message, 'error');
433
+ });
434
+ </script>
435
+ </body>
436
+ </html>
@@ -326,6 +326,13 @@
326
326
  <label for="primaryColor">Primary Color</label>
327
327
  <input type="color" id="primaryColor" value="#7C3AED">
328
328
  </div>
329
+
330
+ <div class="config-group">
331
+ <label style="display: flex; align-items: center; gap: 8px;">
332
+ <input type="checkbox" id="useShadowDOM" checked>
333
+ <span>Use Shadow DOM</span>
334
+ </label>
335
+ </div>
329
336
  </div>
330
337
 
331
338
  <div class="info-box" style="margin-top: 24px;">
@@ -452,11 +459,14 @@
452
459
  }
453
460
 
454
461
  try {
462
+ const useShadowDOM = document.getElementById('useShadowDOM').checked;
463
+
455
464
  const config = {
456
465
  agentId: agentId,
457
466
  appId: appId,
458
467
  language: language,
459
468
  primaryColor: primaryColor,
469
+ useShadowDOM: useShadowDOM,
460
470
  behavior: {
461
471
  mode: mode
462
472
  }
@@ -544,6 +554,7 @@
544
554
  document.getElementById('language').value = 'en';
545
555
  document.getElementById('mode').value = 'unified';
546
556
  document.getElementById('primaryColor').value = '#7C3AED';
557
+ document.getElementById('useShadowDOM').checked = true;
547
558
  // Reset Visual Assistant settings
548
559
  document.getElementById('visualAssistantEnabled').checked = true;
549
560
  document.getElementById('allowHighlight').checked = true;