ttp-agent-sdk 2.48.18 → 2.48.21
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/dist/agent-widget.dev.js +748 -165
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.js +1 -1
- package/package.json +2 -1
- package/dist/examples/pet-store.html +0 -494
- package/dist/examples/seabreeze-hotel.html +0 -403
- package/examples/pet-store.html +0 -494
- package/examples/seabreeze-hotel.html +0 -403
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ttp-agent-sdk",
|
|
3
|
-
"version": "2.48.
|
|
3
|
+
"version": "2.48.21",
|
|
4
4
|
"description": "Comprehensive Voice Agent SDK with Customizable Widget - Real-time audio, WebSocket communication, React components, and extensive customization options",
|
|
5
5
|
"main": "dist/agent-widget.js",
|
|
6
6
|
"module": "dist/agent-widget.esm.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"test:adapters": "npm run test:compile-adapter && npm run test:verify-adapter",
|
|
42
42
|
"test:client-tools": "node --test test/client-tools-registry.test.mjs",
|
|
43
43
|
"test:hop": "node --test test/hop-platform.test.mjs",
|
|
44
|
+
"test:loopback": "node --test test/loopback-aec.test.mjs",
|
|
44
45
|
"clean": "rm -rf dist/",
|
|
45
46
|
"prebuild": "npm run clean"
|
|
46
47
|
},
|
|
@@ -1,494 +0,0 @@
|
|
|
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">
|
|
6
|
-
<title>Paws & Bowls — Dog Food & Toys Delivered</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--ink: #1a2e1f;
|
|
10
|
-
--cream: #faf6ef;
|
|
11
|
-
--green: #2d6a4f;
|
|
12
|
-
--mint: #d8f3dc;
|
|
13
|
-
--orange: #e07a3a;
|
|
14
|
-
--brown: #8b5e3c;
|
|
15
|
-
}
|
|
16
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
17
|
-
body {
|
|
18
|
-
font-family: "Segoe UI", system-ui, sans-serif;
|
|
19
|
-
color: var(--ink);
|
|
20
|
-
background: var(--cream);
|
|
21
|
-
min-height: 100vh;
|
|
22
|
-
}
|
|
23
|
-
header {
|
|
24
|
-
background: linear-gradient(135deg, #1b4332 0%, #2d6a4f 50%, #40916c 100%);
|
|
25
|
-
color: #fff;
|
|
26
|
-
padding: 48px 24px 72px;
|
|
27
|
-
}
|
|
28
|
-
.wrap { max-width: 960px; margin: 0 auto; }
|
|
29
|
-
.header-top {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: flex-start;
|
|
32
|
-
justify-content: space-between;
|
|
33
|
-
gap: 20px;
|
|
34
|
-
flex-wrap: wrap;
|
|
35
|
-
margin-bottom: 20px;
|
|
36
|
-
}
|
|
37
|
-
nav { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
38
|
-
nav a {
|
|
39
|
-
color: #fff;
|
|
40
|
-
text-decoration: none;
|
|
41
|
-
font-size: 13px;
|
|
42
|
-
letter-spacing: 0.06em;
|
|
43
|
-
text-transform: uppercase;
|
|
44
|
-
padding: 8px 14px;
|
|
45
|
-
border: 1px solid rgba(255,255,255,0.35);
|
|
46
|
-
border-radius: 999px;
|
|
47
|
-
}
|
|
48
|
-
nav a.active, nav a:hover {
|
|
49
|
-
background: rgba(255,255,255,0.15);
|
|
50
|
-
border-color: var(--orange);
|
|
51
|
-
}
|
|
52
|
-
.eyebrow {
|
|
53
|
-
letter-spacing: 0.18em;
|
|
54
|
-
text-transform: uppercase;
|
|
55
|
-
font-size: 12px;
|
|
56
|
-
color: #b7e4c7;
|
|
57
|
-
margin-bottom: 10px;
|
|
58
|
-
}
|
|
59
|
-
h1 { font-size: clamp(2rem, 5vw, 3rem); font-weight: 700; line-height: 1.1; }
|
|
60
|
-
h2 { font-size: 1.5rem; font-weight: 600; margin-bottom: 8px; }
|
|
61
|
-
.lead { margin-top: 14px; max-width: 38rem; font-size: 1.05rem; line-height: 1.55; opacity: 0.95; }
|
|
62
|
-
main { margin-top: -36px; padding: 0 24px 80px; }
|
|
63
|
-
.card {
|
|
64
|
-
background: #fff;
|
|
65
|
-
border-radius: 16px;
|
|
66
|
-
padding: 28px;
|
|
67
|
-
box-shadow: 0 16px 40px rgba(26, 46, 31, 0.1);
|
|
68
|
-
}
|
|
69
|
-
.badge {
|
|
70
|
-
display: inline-block;
|
|
71
|
-
background: var(--mint);
|
|
72
|
-
color: var(--green);
|
|
73
|
-
font-size: 12px;
|
|
74
|
-
font-weight: 600;
|
|
75
|
-
padding: 4px 10px;
|
|
76
|
-
border-radius: 999px;
|
|
77
|
-
margin-bottom: 12px;
|
|
78
|
-
}
|
|
79
|
-
.products {
|
|
80
|
-
display: grid;
|
|
81
|
-
gap: 16px;
|
|
82
|
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
83
|
-
margin-top: 20px;
|
|
84
|
-
}
|
|
85
|
-
.product {
|
|
86
|
-
background: var(--cream);
|
|
87
|
-
border-radius: 12px;
|
|
88
|
-
padding: 18px;
|
|
89
|
-
border: 1px solid #e8e0d4;
|
|
90
|
-
}
|
|
91
|
-
.product .tag {
|
|
92
|
-
font-size: 11px;
|
|
93
|
-
text-transform: uppercase;
|
|
94
|
-
letter-spacing: 0.08em;
|
|
95
|
-
color: var(--brown);
|
|
96
|
-
margin-bottom: 6px;
|
|
97
|
-
}
|
|
98
|
-
.product h3 { font-size: 1rem; margin-bottom: 6px; }
|
|
99
|
-
.product p { font-size: 0.9rem; line-height: 1.45; color: #4a5d4e; }
|
|
100
|
-
.product .price { margin-top: 10px; font-weight: 700; color: var(--green); }
|
|
101
|
-
.hint {
|
|
102
|
-
margin-top: 22px;
|
|
103
|
-
padding: 14px 16px;
|
|
104
|
-
border-left: 3px solid var(--orange);
|
|
105
|
-
background: #fff8f0;
|
|
106
|
-
font-size: 14px;
|
|
107
|
-
line-height: 1.45;
|
|
108
|
-
}
|
|
109
|
-
.status { margin-top: 12px; font-family: ui-monospace, monospace; font-size: 12px; color: #5a6b5e; }
|
|
110
|
-
.status.ok { color: #1b6b45; }
|
|
111
|
-
.status.err { color: #9b1c1c; }
|
|
112
|
-
form.delivery {
|
|
113
|
-
display: grid;
|
|
114
|
-
gap: 16px;
|
|
115
|
-
margin-top: 20px;
|
|
116
|
-
}
|
|
117
|
-
.field { display: grid; gap: 6px; }
|
|
118
|
-
.field label {
|
|
119
|
-
font-size: 12px;
|
|
120
|
-
letter-spacing: 0.04em;
|
|
121
|
-
text-transform: uppercase;
|
|
122
|
-
color: #5a6b5e;
|
|
123
|
-
}
|
|
124
|
-
.field input, .field select {
|
|
125
|
-
font: inherit;
|
|
126
|
-
padding: 12px 14px;
|
|
127
|
-
border: 1px solid #d5ddd8;
|
|
128
|
-
border-radius: 10px;
|
|
129
|
-
background: #fff;
|
|
130
|
-
color: var(--ink);
|
|
131
|
-
}
|
|
132
|
-
.field input:focus, .field select:focus {
|
|
133
|
-
outline: 2px solid var(--green);
|
|
134
|
-
outline-offset: 1px;
|
|
135
|
-
}
|
|
136
|
-
.field.just-filled input, .field.just-filled select {
|
|
137
|
-
border-color: var(--orange);
|
|
138
|
-
background: #fff8f0;
|
|
139
|
-
box-shadow: 0 0 0 3px rgba(224, 122, 58, 0.25);
|
|
140
|
-
}
|
|
141
|
-
.actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
|
|
142
|
-
button[type="submit"] {
|
|
143
|
-
font: inherit;
|
|
144
|
-
background: var(--green);
|
|
145
|
-
color: #fff;
|
|
146
|
-
border: 0;
|
|
147
|
-
border-radius: 999px;
|
|
148
|
-
padding: 12px 22px;
|
|
149
|
-
cursor: pointer;
|
|
150
|
-
font-weight: 600;
|
|
151
|
-
}
|
|
152
|
-
button[type="submit"]:hover { background: #1b4332; }
|
|
153
|
-
.confirm {
|
|
154
|
-
display: none;
|
|
155
|
-
margin-top: 18px;
|
|
156
|
-
padding: 16px;
|
|
157
|
-
border-radius: 12px;
|
|
158
|
-
background: #eef7f1;
|
|
159
|
-
color: #1b6b45;
|
|
160
|
-
line-height: 1.45;
|
|
161
|
-
}
|
|
162
|
-
.confirm.show { display: block; }
|
|
163
|
-
.delivery-banner {
|
|
164
|
-
margin-top: 16px;
|
|
165
|
-
padding: 12px 16px;
|
|
166
|
-
background: var(--mint);
|
|
167
|
-
border-radius: 10px;
|
|
168
|
-
font-size: 14px;
|
|
169
|
-
color: var(--green);
|
|
170
|
-
}
|
|
171
|
-
</style>
|
|
172
|
-
</head>
|
|
173
|
-
<body>
|
|
174
|
-
<header>
|
|
175
|
-
<div class="wrap">
|
|
176
|
-
<div class="header-top">
|
|
177
|
-
<p class="eyebrow">Premium dog supplies · Home delivery</p>
|
|
178
|
-
<nav>
|
|
179
|
-
<a href="#home" data-nav="home">Shop</a>
|
|
180
|
-
<a href="#delivery" data-nav="delivery">Order delivery</a>
|
|
181
|
-
</nav>
|
|
182
|
-
</div>
|
|
183
|
-
<h1>Paws & Bowls</h1>
|
|
184
|
-
<p class="lead">Premium dog food and fun toys, delivered to your door. Talk to <strong>Buddy</strong> — say you want a delivery and he will open the form and fill each field as you speak.</p>
|
|
185
|
-
</div>
|
|
186
|
-
</header>
|
|
187
|
-
|
|
188
|
-
<main>
|
|
189
|
-
<div id="view-home" class="wrap card">
|
|
190
|
-
<span class="badge">Free delivery over $50</span>
|
|
191
|
-
<h2>Dog food & toys</h2>
|
|
192
|
-
<p class="delivery-banner">Same-day delivery in Tel Aviv area when you order before noon.</p>
|
|
193
|
-
|
|
194
|
-
<div class="products">
|
|
195
|
-
<div class="product">
|
|
196
|
-
<p class="tag">Dog food</p>
|
|
197
|
-
<h3>Premium Chicken Kibble</h3>
|
|
198
|
-
<p>15 kg bag. All breeds, adult dogs. High protein, no fillers.</p>
|
|
199
|
-
<p class="price">$45</p>
|
|
200
|
-
</div>
|
|
201
|
-
<div class="product">
|
|
202
|
-
<p class="tag">Dog food</p>
|
|
203
|
-
<h3>Grain-Free Salmon</h3>
|
|
204
|
-
<p>12 kg bag. Great for sensitive stomachs and shiny coats.</p>
|
|
205
|
-
<p class="price">$52</p>
|
|
206
|
-
</div>
|
|
207
|
-
<div class="product">
|
|
208
|
-
<p class="tag">Dog food</p>
|
|
209
|
-
<h3>Puppy Growth Formula</h3>
|
|
210
|
-
<p>10 kg bag. Balanced nutrition for puppies up to 12 months.</p>
|
|
211
|
-
<p class="price">$48</p>
|
|
212
|
-
</div>
|
|
213
|
-
<div class="product">
|
|
214
|
-
<p class="tag">Toy</p>
|
|
215
|
-
<h3>Rope Tug Toy</h3>
|
|
216
|
-
<p>Durable cotton rope. Perfect for tug-of-war and dental health.</p>
|
|
217
|
-
<p class="price">$12</p>
|
|
218
|
-
</div>
|
|
219
|
-
<div class="product">
|
|
220
|
-
<p class="tag">Toy</p>
|
|
221
|
-
<h3>Squeaky Ball Set</h3>
|
|
222
|
-
<p>Pack of 3 colorful squeaky balls. Hours of fetch fun.</p>
|
|
223
|
-
<p class="price">$18</p>
|
|
224
|
-
</div>
|
|
225
|
-
<div class="product">
|
|
226
|
-
<p class="tag">Toy</p>
|
|
227
|
-
<h3>Puzzle Treat Dispenser</h3>
|
|
228
|
-
<p>Keeps your dog mentally stimulated while rewarding with treats.</p>
|
|
229
|
-
<p class="price">$24</p>
|
|
230
|
-
</div>
|
|
231
|
-
</div>
|
|
232
|
-
|
|
233
|
-
<p class="hint">Open the TalkToPC widget and say <strong>"I'd like a delivery"</strong> — Buddy will switch to the order form and ask for each detail one at a time.</p>
|
|
234
|
-
<p id="status" class="status">Loading widget…</p>
|
|
235
|
-
</div>
|
|
236
|
-
|
|
237
|
-
<div id="view-delivery" class="wrap card" hidden>
|
|
238
|
-
<span class="badge">Home delivery</span>
|
|
239
|
-
<h2>Order for delivery</h2>
|
|
240
|
-
<p class="hint">Stay on this page and speak with Buddy. After each answer he writes it into the matching field below.</p>
|
|
241
|
-
|
|
242
|
-
<form id="delivery-form" class="delivery">
|
|
243
|
-
<div class="field" data-field="product">
|
|
244
|
-
<label for="field-product">Product</label>
|
|
245
|
-
<select id="field-product" name="product">
|
|
246
|
-
<option value="">Choose a product</option>
|
|
247
|
-
<option value="Premium Chicken Kibble">Premium Chicken Kibble — $45</option>
|
|
248
|
-
<option value="Grain-Free Salmon">Grain-Free Salmon — $52</option>
|
|
249
|
-
<option value="Puppy Growth Formula">Puppy Growth Formula — $48</option>
|
|
250
|
-
<option value="Rope Tug Toy">Rope Tug Toy — $12</option>
|
|
251
|
-
<option value="Squeaky Ball Set">Squeaky Ball Set — $18</option>
|
|
252
|
-
<option value="Puzzle Treat Dispenser">Puzzle Treat Dispenser — $24</option>
|
|
253
|
-
</select>
|
|
254
|
-
</div>
|
|
255
|
-
<div class="field" data-field="quantity">
|
|
256
|
-
<label for="field-quantity">Quantity</label>
|
|
257
|
-
<input id="field-quantity" name="quantity" type="text" inputmode="numeric" placeholder="e.g. 2">
|
|
258
|
-
</div>
|
|
259
|
-
<div class="field" data-field="full_name">
|
|
260
|
-
<label for="field-full_name">Full name</label>
|
|
261
|
-
<input id="field-full_name" name="full_name" type="text" autocomplete="name" placeholder="Your name">
|
|
262
|
-
</div>
|
|
263
|
-
<div class="field" data-field="address">
|
|
264
|
-
<label for="field-address">Delivery address</label>
|
|
265
|
-
<input id="field-address" name="address" type="text" autocomplete="street-address" placeholder="Street, city">
|
|
266
|
-
</div>
|
|
267
|
-
<div class="field" data-field="phone">
|
|
268
|
-
<label for="field-phone">Phone</label>
|
|
269
|
-
<input id="field-phone" name="phone" type="tel" autocomplete="tel" placeholder="Mobile number">
|
|
270
|
-
</div>
|
|
271
|
-
<div class="field" data-field="delivery_time">
|
|
272
|
-
<label for="field-delivery_time">Delivery time preference (optional)</label>
|
|
273
|
-
<select id="field-delivery_time" name="delivery_time">
|
|
274
|
-
<option value="">Any time</option>
|
|
275
|
-
<option value="Morning (9–12)">Morning (9–12)</option>
|
|
276
|
-
<option value="Afternoon (12–5)">Afternoon (12–5)</option>
|
|
277
|
-
<option value="Evening (5–8)">Evening (5–8)</option>
|
|
278
|
-
</select>
|
|
279
|
-
</div>
|
|
280
|
-
<div class="actions">
|
|
281
|
-
<button type="submit">Place order</button>
|
|
282
|
-
</div>
|
|
283
|
-
</form>
|
|
284
|
-
<div id="delivery-confirm" class="confirm" role="status"></div>
|
|
285
|
-
<p id="status-delivery" class="status"></p>
|
|
286
|
-
</div>
|
|
287
|
-
</main>
|
|
288
|
-
|
|
289
|
-
<script src="demo-sdk-loader.js"></script>
|
|
290
|
-
<script>
|
|
291
|
-
const AGENT_ID = 'agent_ddf3e177c';
|
|
292
|
-
const APP_ID = 'app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo';
|
|
293
|
-
const REQUIRED_FIELDS = ['product', 'quantity', 'full_name', 'address', 'phone'];
|
|
294
|
-
const ALL_FIELDS = ['product', 'quantity', 'full_name', 'address', 'phone', 'delivery_time'];
|
|
295
|
-
|
|
296
|
-
const PRODUCT_ALIASES = {
|
|
297
|
-
'chicken': 'Premium Chicken Kibble',
|
|
298
|
-
'chicken kibble': 'Premium Chicken Kibble',
|
|
299
|
-
'premium chicken': 'Premium Chicken Kibble',
|
|
300
|
-
'premium chicken kibble': 'Premium Chicken Kibble',
|
|
301
|
-
'salmon': 'Grain-Free Salmon',
|
|
302
|
-
'grain free salmon': 'Grain-Free Salmon',
|
|
303
|
-
'grain-free salmon': 'Grain-Free Salmon',
|
|
304
|
-
'puppy': 'Puppy Growth Formula',
|
|
305
|
-
'puppy food': 'Puppy Growth Formula',
|
|
306
|
-
'puppy growth': 'Puppy Growth Formula',
|
|
307
|
-
'puppy growth formula': 'Puppy Growth Formula',
|
|
308
|
-
'rope': 'Rope Tug Toy',
|
|
309
|
-
'rope toy': 'Rope Tug Toy',
|
|
310
|
-
'tug toy': 'Rope Tug Toy',
|
|
311
|
-
'rope tug toy': 'Rope Tug Toy',
|
|
312
|
-
'ball': 'Squeaky Ball Set',
|
|
313
|
-
'balls': 'Squeaky Ball Set',
|
|
314
|
-
'squeaky ball': 'Squeaky Ball Set',
|
|
315
|
-
'squeaky balls': 'Squeaky Ball Set',
|
|
316
|
-
'squeaky ball set': 'Squeaky Ball Set',
|
|
317
|
-
'puzzle': 'Puzzle Treat Dispenser',
|
|
318
|
-
'treat dispenser': 'Puzzle Treat Dispenser',
|
|
319
|
-
'puzzle treat dispenser': 'Puzzle Treat Dispenser'
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
function setStatus(msg, kind) {
|
|
323
|
-
['status', 'status-delivery'].forEach((id) => {
|
|
324
|
-
const el = document.getElementById(id);
|
|
325
|
-
if (el) {
|
|
326
|
-
el.textContent = msg;
|
|
327
|
-
el.className = 'status' + (kind ? ' ' + kind : '');
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function showPage(page) {
|
|
333
|
-
const raw = String(page || 'home').trim().toLowerCase();
|
|
334
|
-
const next = raw.includes('deliver') || raw.includes('order') || raw.includes('form')
|
|
335
|
-
? 'delivery'
|
|
336
|
-
: 'home';
|
|
337
|
-
document.getElementById('view-home').hidden = next !== 'home';
|
|
338
|
-
document.getElementById('view-delivery').hidden = next !== 'delivery';
|
|
339
|
-
document.querySelectorAll('nav [data-nav]').forEach((link) => {
|
|
340
|
-
link.classList.toggle('active', link.getAttribute('data-nav') === next);
|
|
341
|
-
});
|
|
342
|
-
if (location.hash !== '#' + next) {
|
|
343
|
-
history.replaceState(null, '', '#' + next);
|
|
344
|
-
}
|
|
345
|
-
if (next === 'delivery') {
|
|
346
|
-
const view = document.getElementById('view-delivery');
|
|
347
|
-
if (view) view.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
348
|
-
}
|
|
349
|
-
return next;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function formValues() {
|
|
353
|
-
const values = {};
|
|
354
|
-
ALL_FIELDS.forEach((name) => {
|
|
355
|
-
const el = document.getElementById('field-' + name);
|
|
356
|
-
values[name] = el ? String(el.value || '').trim() : '';
|
|
357
|
-
});
|
|
358
|
-
return values;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function formSnapshot() {
|
|
362
|
-
const values = formValues();
|
|
363
|
-
const filled = REQUIRED_FIELDS.filter((name) => values[name]);
|
|
364
|
-
const missing = REQUIRED_FIELDS.filter((name) => !values[name]);
|
|
365
|
-
return { values, filled, missing };
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function normalizeProduct(value) {
|
|
369
|
-
const raw = String(value || '').trim();
|
|
370
|
-
const key = raw.toLowerCase();
|
|
371
|
-
if (PRODUCT_ALIASES[key]) return PRODUCT_ALIASES[key];
|
|
372
|
-
const options = Array.from(document.getElementById('field-product').options)
|
|
373
|
-
.map((o) => o.value)
|
|
374
|
-
.filter(Boolean);
|
|
375
|
-
const match = options.find((opt) => opt.toLowerCase().includes(key) || key.includes(opt.toLowerCase()));
|
|
376
|
-
return match || raw;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function normalizeDeliveryTime(value) {
|
|
380
|
-
const raw = String(value || '').trim().toLowerCase();
|
|
381
|
-
if (!raw) return '';
|
|
382
|
-
if (raw.includes('morning') || raw.includes('9') || raw.includes('morn')) return 'Morning (9–12)';
|
|
383
|
-
if (raw.includes('afternoon') || raw.includes('12') || raw.includes('after')) return 'Afternoon (12–5)';
|
|
384
|
-
if (raw.includes('evening') || raw.includes('5') || raw.includes('night')) return 'Evening (5–8)';
|
|
385
|
-
return String(value || '').trim();
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function fillDeliveryField(field, value) {
|
|
389
|
-
if (!ALL_FIELDS.includes(field)) {
|
|
390
|
-
return { ok: false, error: 'Unknown field', field, allowed: ALL_FIELDS };
|
|
391
|
-
}
|
|
392
|
-
showPage('delivery');
|
|
393
|
-
const wrap = document.querySelector('[data-field="' + field + '"]');
|
|
394
|
-
const el = document.getElementById('field-' + field);
|
|
395
|
-
let nextValue = String(value || '').trim();
|
|
396
|
-
if (field === 'product') nextValue = normalizeProduct(value);
|
|
397
|
-
if (field === 'delivery_time') nextValue = normalizeDeliveryTime(value);
|
|
398
|
-
if (!el) {
|
|
399
|
-
return { ok: false, error: 'Field is missing from the page', field };
|
|
400
|
-
}
|
|
401
|
-
el.value = nextValue;
|
|
402
|
-
if (wrap) {
|
|
403
|
-
wrap.classList.add('just-filled');
|
|
404
|
-
setTimeout(() => wrap.classList.remove('just-filled'), 1600);
|
|
405
|
-
}
|
|
406
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
407
|
-
const snap = formSnapshot();
|
|
408
|
-
return { ok: true, field, value: nextValue, filled: snap.filled, missing: snap.missing };
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function submitDelivery() {
|
|
412
|
-
showPage('delivery');
|
|
413
|
-
const snap = formSnapshot();
|
|
414
|
-
if (snap.missing.length) {
|
|
415
|
-
return { ok: false, error: 'Form is incomplete', missing: snap.missing, filled: snap.filled };
|
|
416
|
-
}
|
|
417
|
-
const box = document.getElementById('delivery-confirm');
|
|
418
|
-
const time = snap.values.delivery_time ? ' Preferred time: ' + snap.values.delivery_time + '.' : '';
|
|
419
|
-
box.textContent = 'Order placed for ' + snap.values.full_name + ': ' + snap.values.quantity + 'x ' + snap.values.product + ' to ' + snap.values.address + '.' + time + ' Buddy will have it delivered within 24 hours. (Demo — no real charge.)';
|
|
420
|
-
box.classList.add('show');
|
|
421
|
-
return { ok: true, submitted: true, values: snap.values };
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function registerHandler(widget, names, handler) {
|
|
425
|
-
names.forEach((name) => widget.registerToolHandler(name, handler));
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function registerSiteTools(widget) {
|
|
429
|
-
registerHandler(widget, ['pet_navigate_page', 'pet_navigate_page_imported'], async (params) => {
|
|
430
|
-
const page = showPage(params && params.page);
|
|
431
|
-
return { ok: true, page };
|
|
432
|
-
});
|
|
433
|
-
registerHandler(widget, ['fill_delivery_field', 'fill_delivery_field_imported'], async (params) => {
|
|
434
|
-
const field = params && (params.field || params.name);
|
|
435
|
-
const value = params && (params.value != null ? params.value : params.text);
|
|
436
|
-
return fillDeliveryField(field, value);
|
|
437
|
-
});
|
|
438
|
-
registerHandler(widget, ['submit_delivery', 'submit_delivery_imported'], async () => submitDelivery());
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function startWidget() {
|
|
442
|
-
const TTPChatWidget = window.TTPAgentSDK && window.TTPAgentSDK.TTPChatWidget;
|
|
443
|
-
if (!TTPChatWidget) {
|
|
444
|
-
setStatus('SDK loaded but TTPChatWidget is missing.', 'err');
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
if (window.widgetInstance) {
|
|
448
|
-
try { window.widgetInstance.destroy(); } catch (_) {}
|
|
449
|
-
}
|
|
450
|
-
const config = window.TTPDemoSdkLoader.applyWebSocketUrl({
|
|
451
|
-
agentId: AGENT_ID,
|
|
452
|
-
appId: APP_ID,
|
|
453
|
-
language: 'en',
|
|
454
|
-
agentName: 'Buddy',
|
|
455
|
-
agentRole: 'Pet Store Assistant',
|
|
456
|
-
headline: 'Welcome to Paws & Bowls',
|
|
457
|
-
subline: 'Ask about dog food and toys, or say you want a delivery.',
|
|
458
|
-
behavior: { mode: 'unified', startOpen: true },
|
|
459
|
-
icon: { type: 'custom', size: 'medium', backgroundColor: '#2d6a4f' }
|
|
460
|
-
});
|
|
461
|
-
window.widgetInstance = new TTPChatWidget(config);
|
|
462
|
-
registerSiteTools(window.widgetInstance);
|
|
463
|
-
setStatus('Buddy is ready. Say "I want a delivery" to start your order.', 'ok');
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
document.querySelectorAll('nav [data-nav]').forEach((link) => {
|
|
467
|
-
link.addEventListener('click', (event) => {
|
|
468
|
-
event.preventDefault();
|
|
469
|
-
showPage(link.getAttribute('data-nav'));
|
|
470
|
-
});
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
document.getElementById('delivery-form').addEventListener('submit', (event) => {
|
|
474
|
-
event.preventDefault();
|
|
475
|
-
const result = submitDelivery();
|
|
476
|
-
if (!result.ok) {
|
|
477
|
-
setStatus('Still missing: ' + result.missing.join(', '), 'err');
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
window.addEventListener('hashchange', () => {
|
|
482
|
-
showPage(location.hash.replace('#', '') || 'home');
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
showPage(location.hash.replace('#', '') || 'home');
|
|
486
|
-
|
|
487
|
-
window.TTPDemoSdkLoader.waitForReady()
|
|
488
|
-
.then(startWidget)
|
|
489
|
-
.catch((err) => setStatus(String(err && err.message || err), 'err'));
|
|
490
|
-
|
|
491
|
-
window.addEventListener('ttp-demo-sdk-ready', startWidget);
|
|
492
|
-
</script>
|
|
493
|
-
</body>
|
|
494
|
-
</html>
|