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
|
@@ -1,403 +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>Seabreeze Hotel — Concierge</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--ink: #14202b;
|
|
10
|
-
--sand: #f4efe6;
|
|
11
|
-
--sea: #1c4e6b;
|
|
12
|
-
--foam: #e8f2f6;
|
|
13
|
-
--gold: #c4a46a;
|
|
14
|
-
}
|
|
15
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
16
|
-
body {
|
|
17
|
-
font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
|
|
18
|
-
color: var(--ink);
|
|
19
|
-
background: var(--sand);
|
|
20
|
-
min-height: 100vh;
|
|
21
|
-
}
|
|
22
|
-
header {
|
|
23
|
-
background: linear-gradient(160deg, #0d2c3d 0%, #1c4e6b 55%, #3a7a8c 100%);
|
|
24
|
-
color: #f7f3ea;
|
|
25
|
-
padding: 56px 24px 88px;
|
|
26
|
-
}
|
|
27
|
-
.wrap { max-width: 920px; margin: 0 auto; }
|
|
28
|
-
.header-top {
|
|
29
|
-
display: flex;
|
|
30
|
-
align-items: flex-start;
|
|
31
|
-
justify-content: space-between;
|
|
32
|
-
gap: 20px;
|
|
33
|
-
flex-wrap: wrap;
|
|
34
|
-
}
|
|
35
|
-
nav {
|
|
36
|
-
display: flex;
|
|
37
|
-
gap: 8px;
|
|
38
|
-
font-family: system-ui, sans-serif;
|
|
39
|
-
}
|
|
40
|
-
nav a {
|
|
41
|
-
color: #f7f3ea;
|
|
42
|
-
text-decoration: none;
|
|
43
|
-
font-size: 13px;
|
|
44
|
-
letter-spacing: 0.08em;
|
|
45
|
-
text-transform: uppercase;
|
|
46
|
-
padding: 8px 14px;
|
|
47
|
-
border: 1px solid rgba(247, 243, 234, 0.28);
|
|
48
|
-
border-radius: 999px;
|
|
49
|
-
}
|
|
50
|
-
nav a.active, nav a:hover {
|
|
51
|
-
background: rgba(247, 243, 234, 0.14);
|
|
52
|
-
border-color: var(--gold);
|
|
53
|
-
color: #fff;
|
|
54
|
-
}
|
|
55
|
-
.eyebrow {
|
|
56
|
-
letter-spacing: 0.22em;
|
|
57
|
-
text-transform: uppercase;
|
|
58
|
-
font-size: 12px;
|
|
59
|
-
color: var(--gold);
|
|
60
|
-
margin-bottom: 12px;
|
|
61
|
-
}
|
|
62
|
-
h1 { font-size: clamp(2.2rem, 5vw, 3.4rem); font-weight: 500; line-height: 1.15; }
|
|
63
|
-
h2 { font-size: 1.55rem; font-weight: 500; }
|
|
64
|
-
.lead { margin-top: 16px; max-width: 36rem; font-size: 1.15rem; line-height: 1.55; opacity: 0.92; }
|
|
65
|
-
main { margin-top: -40px; padding: 0 24px 80px; }
|
|
66
|
-
.card {
|
|
67
|
-
background: #fff;
|
|
68
|
-
border-radius: 18px;
|
|
69
|
-
padding: 28px;
|
|
70
|
-
box-shadow: 0 18px 40px rgba(20, 32, 43, 0.08);
|
|
71
|
-
}
|
|
72
|
-
.rooms { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin-top: 20px; }
|
|
73
|
-
.room { background: var(--foam); border-radius: 12px; padding: 16px; }
|
|
74
|
-
.room h3 { font-size: 1.05rem; margin-bottom: 6px; }
|
|
75
|
-
.room p { font-size: 0.95rem; line-height: 1.45; color: #3d4d5a; }
|
|
76
|
-
.hint {
|
|
77
|
-
margin-top: 22px;
|
|
78
|
-
padding: 14px 16px;
|
|
79
|
-
border-left: 3px solid var(--gold);
|
|
80
|
-
background: #fbf7ef;
|
|
81
|
-
font-family: system-ui, sans-serif;
|
|
82
|
-
font-size: 14px;
|
|
83
|
-
line-height: 1.45;
|
|
84
|
-
}
|
|
85
|
-
.status { margin-top: 12px; font-family: ui-monospace, monospace; font-size: 12px; color: #4b5d6b; }
|
|
86
|
-
.status.ok { color: #1b6b45; }
|
|
87
|
-
.status.err { color: #9b1c1c; }
|
|
88
|
-
form.booking {
|
|
89
|
-
display: grid;
|
|
90
|
-
gap: 16px;
|
|
91
|
-
margin-top: 20px;
|
|
92
|
-
font-family: system-ui, sans-serif;
|
|
93
|
-
}
|
|
94
|
-
.field {
|
|
95
|
-
display: grid;
|
|
96
|
-
gap: 6px;
|
|
97
|
-
}
|
|
98
|
-
.field label {
|
|
99
|
-
font-size: 13px;
|
|
100
|
-
letter-spacing: 0.04em;
|
|
101
|
-
text-transform: uppercase;
|
|
102
|
-
color: #4b5d6b;
|
|
103
|
-
}
|
|
104
|
-
.field input, .field select {
|
|
105
|
-
font: inherit;
|
|
106
|
-
padding: 12px 14px;
|
|
107
|
-
border: 1px solid #d5dde3;
|
|
108
|
-
border-radius: 10px;
|
|
109
|
-
background: #fff;
|
|
110
|
-
color: var(--ink);
|
|
111
|
-
}
|
|
112
|
-
.field input:focus, .field select:focus {
|
|
113
|
-
outline: 2px solid #1c4e6b;
|
|
114
|
-
outline-offset: 1px;
|
|
115
|
-
}
|
|
116
|
-
.field.just-filled input, .field.just-filled select {
|
|
117
|
-
border-color: #c4a46a;
|
|
118
|
-
background: #fff8ea;
|
|
119
|
-
box-shadow: 0 0 0 3px rgba(196, 164, 106, 0.28);
|
|
120
|
-
}
|
|
121
|
-
.actions {
|
|
122
|
-
display: flex;
|
|
123
|
-
gap: 12px;
|
|
124
|
-
align-items: center;
|
|
125
|
-
flex-wrap: wrap;
|
|
126
|
-
}
|
|
127
|
-
button[type="submit"] {
|
|
128
|
-
font: inherit;
|
|
129
|
-
background: var(--sea);
|
|
130
|
-
color: #f7f3ea;
|
|
131
|
-
border: 0;
|
|
132
|
-
border-radius: 999px;
|
|
133
|
-
padding: 12px 20px;
|
|
134
|
-
cursor: pointer;
|
|
135
|
-
}
|
|
136
|
-
.confirm {
|
|
137
|
-
display: none;
|
|
138
|
-
margin-top: 18px;
|
|
139
|
-
padding: 16px;
|
|
140
|
-
border-radius: 12px;
|
|
141
|
-
background: #eef7f1;
|
|
142
|
-
color: #1b6b45;
|
|
143
|
-
font-family: system-ui, sans-serif;
|
|
144
|
-
line-height: 1.45;
|
|
145
|
-
}
|
|
146
|
-
.confirm.show { display: block; }
|
|
147
|
-
</style>
|
|
148
|
-
</head>
|
|
149
|
-
<body>
|
|
150
|
-
<header>
|
|
151
|
-
<div class="wrap">
|
|
152
|
-
<div class="header-top">
|
|
153
|
-
<p class="eyebrow">Praia do Guincho · Cascais</p>
|
|
154
|
-
<nav>
|
|
155
|
-
<a href="#home" data-nav="home">Rooms</a>
|
|
156
|
-
<a href="#booking" data-nav="booking">Request a stay</a>
|
|
157
|
-
</nav>
|
|
158
|
-
</div>
|
|
159
|
-
<h1>Seabreeze Hotel</h1>
|
|
160
|
-
<p class="lead">A 42-room boutique stay above the Atlantic. Ask Mila about rooms, or say you want to book — she will open the form and fill each field as you speak.</p>
|
|
161
|
-
</div>
|
|
162
|
-
</header>
|
|
163
|
-
<main>
|
|
164
|
-
<div id="view-home" class="wrap card">
|
|
165
|
-
<p class="eyebrow">Stay</p>
|
|
166
|
-
<h2>Rooms overlooking the cliff</h2>
|
|
167
|
-
<div class="rooms">
|
|
168
|
-
<div class="room">
|
|
169
|
-
<h3>Garden Room</h3>
|
|
170
|
-
<p>24 m², garden view, queen bed. Quiet and close to the path down to the beach.</p>
|
|
171
|
-
</div>
|
|
172
|
-
<div class="room">
|
|
173
|
-
<h3>Ocean Room</h3>
|
|
174
|
-
<p>30 m², balcony with a full Atlantic view, king bed.</p>
|
|
175
|
-
</div>
|
|
176
|
-
<div class="room">
|
|
177
|
-
<h3>Cliff Suite</h3>
|
|
178
|
-
<p>48 m², living room, soaking tub, and a large terrace.</p>
|
|
179
|
-
</div>
|
|
180
|
-
<div class="room">
|
|
181
|
-
<h3>Lighthouse Penthouse</h3>
|
|
182
|
-
<p>70 m², private rooftop plunge pool, sleeps four.</p>
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
<p class="hint">Tap TalkToPC and speak with <strong>Mila</strong>. Say you want to book a stay — she opens this page’s form and fills one field at a time from what you say.</p>
|
|
186
|
-
<p id="status" class="status">Loading widget…</p>
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
<div id="view-booking" class="wrap card" hidden>
|
|
190
|
-
<p class="eyebrow">Request</p>
|
|
191
|
-
<h2>Tell Mila each detail</h2>
|
|
192
|
-
<p class="hint">Stay on this page and speak. After each answer she writes it into the matching field. Required: check-in, nights, guests, room, name, and email or phone.</p>
|
|
193
|
-
<form id="booking-form" class="booking">
|
|
194
|
-
<div class="field" data-field="check_in">
|
|
195
|
-
<label for="field-check_in">Check-in date</label>
|
|
196
|
-
<input id="field-check_in" name="check_in" type="text" autocomplete="off" placeholder="For example 12 September">
|
|
197
|
-
</div>
|
|
198
|
-
<div class="field" data-field="nights">
|
|
199
|
-
<label for="field-nights">Nights</label>
|
|
200
|
-
<input id="field-nights" name="nights" type="text" inputmode="numeric" placeholder="Number of nights">
|
|
201
|
-
</div>
|
|
202
|
-
<div class="field" data-field="guests">
|
|
203
|
-
<label for="field-guests">Guests</label>
|
|
204
|
-
<input id="field-guests" name="guests" type="text" inputmode="numeric" placeholder="Number of guests">
|
|
205
|
-
</div>
|
|
206
|
-
<div class="field" data-field="room_type">
|
|
207
|
-
<label for="field-room_type">Room type</label>
|
|
208
|
-
<select id="field-room_type" name="room_type">
|
|
209
|
-
<option value="">Choose a room</option>
|
|
210
|
-
<option value="Garden Room">Garden Room</option>
|
|
211
|
-
<option value="Ocean Room">Ocean Room</option>
|
|
212
|
-
<option value="Cliff Suite">Cliff Suite</option>
|
|
213
|
-
<option value="Lighthouse Penthouse">Lighthouse Penthouse</option>
|
|
214
|
-
</select>
|
|
215
|
-
</div>
|
|
216
|
-
<div class="field" data-field="full_name">
|
|
217
|
-
<label for="field-full_name">Full name</label>
|
|
218
|
-
<input id="field-full_name" name="full_name" type="text" autocomplete="name" placeholder="Guest name">
|
|
219
|
-
</div>
|
|
220
|
-
<div class="field" data-field="contact">
|
|
221
|
-
<label for="field-contact">Email or phone</label>
|
|
222
|
-
<input id="field-contact" name="contact" type="text" autocomplete="off" placeholder="Email or phone number">
|
|
223
|
-
</div>
|
|
224
|
-
<div class="actions">
|
|
225
|
-
<button type="submit">Send request</button>
|
|
226
|
-
</div>
|
|
227
|
-
</form>
|
|
228
|
-
<div id="booking-confirm" class="confirm" role="status"></div>
|
|
229
|
-
<p id="status-booking" class="status"></p>
|
|
230
|
-
</div>
|
|
231
|
-
</main>
|
|
232
|
-
|
|
233
|
-
<script src="demo-sdk-loader.js"></script>
|
|
234
|
-
<script>
|
|
235
|
-
const AGENT_ID = 'agent_3160ec419';
|
|
236
|
-
const APP_ID = 'app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo';
|
|
237
|
-
const FIELDS = ['check_in', 'nights', 'guests', 'room_type', 'full_name', 'contact'];
|
|
238
|
-
const ROOM_ALIASES = {
|
|
239
|
-
'garden': 'Garden Room',
|
|
240
|
-
'garden room': 'Garden Room',
|
|
241
|
-
'ocean': 'Ocean Room',
|
|
242
|
-
'ocean room': 'Ocean Room',
|
|
243
|
-
'cliff': 'Cliff Suite',
|
|
244
|
-
'cliff suite': 'Cliff Suite',
|
|
245
|
-
'suite': 'Cliff Suite',
|
|
246
|
-
'lighthouse': 'Lighthouse Penthouse',
|
|
247
|
-
'penthouse': 'Lighthouse Penthouse',
|
|
248
|
-
'lighthouse penthouse': 'Lighthouse Penthouse'
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
function setStatus(msg, kind) {
|
|
252
|
-
const el = document.getElementById('status');
|
|
253
|
-
if (el) {
|
|
254
|
-
el.textContent = msg;
|
|
255
|
-
el.className = 'status' + (kind ? ' ' + kind : '');
|
|
256
|
-
}
|
|
257
|
-
const booking = document.getElementById('status-booking');
|
|
258
|
-
if (booking) {
|
|
259
|
-
booking.textContent = msg;
|
|
260
|
-
booking.className = 'status' + (kind ? ' ' + kind : '');
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function currentPage() {
|
|
265
|
-
return document.getElementById('view-booking').hidden ? 'home' : 'booking';
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function showPage(page) {
|
|
269
|
-
const next = page === 'booking' ? 'booking' : 'home';
|
|
270
|
-
document.getElementById('view-home').hidden = next !== 'home';
|
|
271
|
-
document.getElementById('view-booking').hidden = next !== 'booking';
|
|
272
|
-
document.querySelectorAll('nav [data-nav]').forEach((link) => {
|
|
273
|
-
link.classList.toggle('active', link.getAttribute('data-nav') === next);
|
|
274
|
-
});
|
|
275
|
-
if (location.hash !== '#' + next) {
|
|
276
|
-
history.replaceState(null, '', '#' + next);
|
|
277
|
-
}
|
|
278
|
-
return next;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function formValues() {
|
|
282
|
-
const values = {};
|
|
283
|
-
FIELDS.forEach((name) => {
|
|
284
|
-
const el = document.getElementById('field-' + name);
|
|
285
|
-
values[name] = el ? String(el.value || '').trim() : '';
|
|
286
|
-
});
|
|
287
|
-
return values;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function formSnapshot() {
|
|
291
|
-
const values = formValues();
|
|
292
|
-
const filled = FIELDS.filter((name) => values[name]);
|
|
293
|
-
const missing = FIELDS.filter((name) => !values[name]);
|
|
294
|
-
return { values, filled, missing };
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function normalizeRoom(value) {
|
|
298
|
-
const raw = String(value || '').trim();
|
|
299
|
-
const key = raw.toLowerCase();
|
|
300
|
-
return ROOM_ALIASES[key] || raw;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function fillBookingField(field, value) {
|
|
304
|
-
if (!FIELDS.includes(field)) {
|
|
305
|
-
return { ok: false, error: 'Unknown field', field, allowed: FIELDS };
|
|
306
|
-
}
|
|
307
|
-
showPage('booking');
|
|
308
|
-
const wrap = document.querySelector('[data-field="' + field + '"]');
|
|
309
|
-
const el = document.getElementById('field-' + field);
|
|
310
|
-
const nextValue = field === 'room_type' ? normalizeRoom(value) : String(value || '').trim();
|
|
311
|
-
if (!el) {
|
|
312
|
-
return { ok: false, error: 'Field is missing from the page', field };
|
|
313
|
-
}
|
|
314
|
-
el.value = nextValue;
|
|
315
|
-
if (wrap) {
|
|
316
|
-
wrap.classList.add('just-filled');
|
|
317
|
-
setTimeout(() => wrap.classList.remove('just-filled'), 1600);
|
|
318
|
-
}
|
|
319
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
320
|
-
const snap = formSnapshot();
|
|
321
|
-
return { ok: true, field, value: nextValue, filled: snap.filled, missing: snap.missing };
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function submitBooking() {
|
|
325
|
-
showPage('booking');
|
|
326
|
-
const snap = formSnapshot();
|
|
327
|
-
if (snap.missing.length) {
|
|
328
|
-
return { ok: false, error: 'Form is incomplete', missing: snap.missing, filled: snap.filled };
|
|
329
|
-
}
|
|
330
|
-
const box = document.getElementById('booking-confirm');
|
|
331
|
-
box.textContent = 'Request received for ' + snap.values.full_name + '. The front desk will email a confirmation shortly. This demo does not create a real reservation.';
|
|
332
|
-
box.classList.add('show');
|
|
333
|
-
return { ok: true, submitted: true, values: snap.values };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function registerSiteTools(widget) {
|
|
337
|
-
widget.registerToolHandler('navigate_page', async (params) => {
|
|
338
|
-
const page = showPage(params && params.page);
|
|
339
|
-
return { ok: true, page };
|
|
340
|
-
});
|
|
341
|
-
widget.registerToolHandler('fill_booking_field', async (params) => {
|
|
342
|
-
const field = params && (params.field || params.name);
|
|
343
|
-
const value = params && (params.value != null ? params.value : params.text);
|
|
344
|
-
return fillBookingField(field, value);
|
|
345
|
-
});
|
|
346
|
-
widget.registerToolHandler('submit_booking', async () => submitBooking());
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function startWidget() {
|
|
350
|
-
const TTPChatWidget = window.TTPAgentSDK && window.TTPAgentSDK.TTPChatWidget;
|
|
351
|
-
if (!TTPChatWidget) {
|
|
352
|
-
setStatus('SDK loaded but TTPChatWidget is missing.', 'err');
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
if (window.widgetInstance) {
|
|
356
|
-
try { window.widgetInstance.destroy(); } catch (_) {}
|
|
357
|
-
}
|
|
358
|
-
const config = window.TTPDemoSdkLoader.applyWebSocketUrl({
|
|
359
|
-
agentId: AGENT_ID,
|
|
360
|
-
appId: APP_ID,
|
|
361
|
-
language: 'en',
|
|
362
|
-
agentName: 'Mila',
|
|
363
|
-
agentRole: 'Seabreeze Concierge',
|
|
364
|
-
headline: 'Welcome to Seabreeze',
|
|
365
|
-
subline: 'Ask about rooms, or say you want to book a stay.',
|
|
366
|
-
behavior: { mode: 'unified', startOpen: true },
|
|
367
|
-
flavor: { type: 'hotels', partnerId: 'mock-hotel', callView: 'minimized' },
|
|
368
|
-
icon: { type: 'custom', size: 'medium', backgroundColor: '#FFFFFF' }
|
|
369
|
-
});
|
|
370
|
-
window.widgetInstance = new TTPChatWidget(config);
|
|
371
|
-
registerSiteTools(window.widgetInstance);
|
|
372
|
-
setStatus('Mila is ready. Say you want to book, then speak one field at a time.', 'ok');
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
document.querySelectorAll('nav [data-nav]').forEach((link) => {
|
|
376
|
-
link.addEventListener('click', (event) => {
|
|
377
|
-
event.preventDefault();
|
|
378
|
-
showPage(link.getAttribute('data-nav'));
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
document.getElementById('booking-form').addEventListener('submit', (event) => {
|
|
383
|
-
event.preventDefault();
|
|
384
|
-
const result = submitBooking();
|
|
385
|
-
if (!result.ok) {
|
|
386
|
-
setStatus('Still missing: ' + result.missing.join(', '), 'err');
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
window.addEventListener('hashchange', () => {
|
|
391
|
-
showPage(location.hash.replace('#', '') || 'home');
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
showPage(location.hash.replace('#', '') || 'home');
|
|
395
|
-
|
|
396
|
-
window.TTPDemoSdkLoader.waitForReady()
|
|
397
|
-
.then(startWidget)
|
|
398
|
-
.catch((err) => setStatus(String(err && err.message || err), 'err'));
|
|
399
|
-
|
|
400
|
-
window.addEventListener('ttp-demo-sdk-ready', startWidget);
|
|
401
|
-
</script>
|
|
402
|
-
</body>
|
|
403
|
-
</html>
|