ttp-agent-sdk 2.40.0 → 2.44.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.
@@ -12,8 +12,8 @@ class AudioProcessor extends AudioWorkletProcessor {
12
12
  // Configuration
13
13
  this.config = options.processorOptions || {};
14
14
  // Use AudioContext sampleRate (available as global 'sampleRate' in AudioWorkletProcessor)
15
- // Fall back to config if sampleRate not available, default to 44100 Hz to match server output
16
- this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 44100);
15
+ // Fall back to config if sampleRate not available, default to 24 kHz to match typical server/TTS output
16
+ this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 24000);
17
17
  this.bufferSize = 128; // Process 128 samples at a time (256 bytes = 8ms at 16kHz)
18
18
  this.buffer = new Float32Array(this.bufferSize);
19
19
  this.bufferIndex = 0;
@@ -44,6 +44,10 @@ class AudioProcessor extends AudioWorkletProcessor {
44
44
  // Batching buffer
45
45
  this.sendBuffer = null;
46
46
  this.sendBufferBytes = 0;
47
+
48
+ // Start true so the first process() call cannot terminate the worklet before
49
+ // the main thread posts setForceContinuous (WebKit/iOS often renders audio before port messages are handled).
50
+ this.isProcessing = true;
47
51
 
48
52
  // Handle messages from main thread
49
53
  this.port.onmessage = (event) => {
@@ -0,0 +1 @@
1
+ !function(){"use strict";var e=["google.com","googleapis.com","gstatic.com","googletagmanager.com","google-analytics.com","googlesyndication.com","doubleclick.net","facebook.net","facebook.com","fbcdn.net","connect.facebook","cdn.jsdelivr.net","jsdelivr.net","unpkg.com","cdnjs.cloudflare.com","hotjar.com","segment.com","segment.io","mixpanel.com","clarity.ms","licdn.com","bat.bing.com","pinterest.com","tiktok.com","tiktokcdn.com","criteo.com","taboola.com","outbrain.com","cloudflareinsights.com","browser.sentry-cdn.com","sentry.io","amazon-adsystem.com","adsafeprotected.com","scorecardresearch.com","snapchat.com","twitter.com","twimg.com","x.com"];function o(e){if(!e||"string"!=typeof e)return!1;var o=e.trim();return!!o&&(-1!==o.indexOf("fetch")||-1!==o.indexOf("axios")||-1!==o.indexOf("ajax")||-1!==o.indexOf("api")||-1!==o.indexOf("search")||-1!==o.indexOf("query")||-1!==o.indexOf("חיפוש"))}function n(o,n){try{var r=new URL(o,window.location.href);if(function(o){if(!o)return!0;for(var n=String(o).toLowerCase(),r=0;r<e.length;r++)if(n===e[r]||n.endsWith("."+e[r]))return!0;return!1}(r.hostname))return!1;var t=window.location.hostname;if(!t)return!1;if(r.hostname===t)return!0;if(r.hostname.endsWith("."+t))return!0;if(-1!==o.indexOf(t))return!0;var c=n||"";return"/"===c.charAt(0)||0===c.indexOf("./")}catch(e){return!1}}function r(e){if(null==e)return"";var o=String(e);return o.length<=500?o:o.slice(0,500)+"…"}window.ttpDiscover=function(){var e="",t="",c=(new Date).toISOString(),i=[],s=[];try{e=window.location.href,t=window.location.hostname;var l=[];try{l=document.querySelectorAll("script:not([src])")}catch(e){l=[]}for(var a=0;a<l.length;a++)try{var u=(l[a].textContent||"").trim();if(!o(u))continue;i.push(u)}catch(e){}var p=[];try{p=document.querySelectorAll("script[src]")}catch(e){p=[]}for(var g={},d=0;d<p.length;d++)try{var h=p[d],m=h.getAttribute("src")||"",f=h.src||"";if(!n(f,m))continue;if(g[f])continue;g[f]=!0,s.push(f)}catch(e){}}catch(o){return console.error("[TTP Discovery] Unexpected error while scanning:",o),{pageUrl:e||function(){try{return window.location.href}catch(e){return""}}(),hostname:t||function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:c,inlineScripts:[],scriptUrls:[],error:String(o&&o.message?o.message:o)}}var v={pageUrl:e,hostname:t,discoveredAt:c,inlineScripts:i,scriptUrls:s};try{console.group("🔍 TTP GO Site Discovery"),console.log("Starting discovery on: "+e),console.group("📄 Inline Scripts"),console.log("Found "+i.length+" relevant inline scripts");for(var y=0;y<i.length;y++)console.log("--- Inline Script #"+(y+1)+" ---"),console.log(r(i[y]));console.groupEnd(),console.group("🔗 Script URLs"),console.log("Found "+s.length+" same-domain script files");for(var w=0;w<s.length;w++)console.log(" - "+s[w]);console.groupEnd(),console.group("📦 Full Payload (ready to send to backend)");try{console.log(JSON.stringify(v,null,2))}catch(e){console.log(v)}console.groupEnd(),console.log("✅ Discovery complete. Call ttpDiscover() again anytime."),console.groupEnd()}catch(e){try{console.log(v)}catch(e){}}return v},window.ttpDiscoverFull=async function(){try{console.group("🚀 TTP GO Discovery Full");var e=await Promise.resolve(window.ttpDiscover()),o=["fetch(","axios.","$.ajax","XMLHttpRequest","/api/","/search","/products","?q=","?search=","?s=","search=","query=","חיפוש"],n=5e5,r=[];console.group("📂 Fetching JS files");for(var t=0;t<e.scriptUrls.length;t++){var c=e.scriptUrls[t];console.log("Fetching:",c);try{var i,s=new AbortController,l=setTimeout(function(){s.abort()},5e3);try{i=await fetch(c,{signal:s.signal})}finally{clearTimeout(l)}if(!i.ok){console.warn("⚠️ HTTP "+i.status+": "+c),r.push({url:c,skipped:!0,reason:"http_"+i.status,snippets:[]});continue}var a=i.headers.get("content-length");if(a&&parseInt(a,10)>n){console.warn("⚠️ Skipped (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}var u=await i.text();if(u.length>n){console.warn("⚠️ Skipped after read (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}for(var p=u.split("\n"),g=[],d=0;d<p.length;d++){for(var h=p[d],m=!1,f=0;f<o.length;f++)if(-1!==h.indexOf(o[f])){m=!0;break}if(m){for(var v=Math.max(0,d-2),y=Math.min(p.length-1,d+2),w=[],x=0;x<o.length;x++)-1!==h.indexOf(o[x])&&w.push(o[x]);g.push({lineNumber:d+1,matchedKeywords:w,context:p.slice(v,y+1).join("\n")})}}console.log(" → "+g.length+" matches found");for(var S=0;S<g.length;S++){var b=g[S];console.group(" Line "+b.lineNumber+" — ["+b.matchedKeywords.join(", ")+"]"),console.log(b.context),console.groupEnd()}r.push({url:c,skipped:!1,snippets:g})}catch(e){e&&"AbortError"===e.name?(console.warn("⚠️ Timeout: "+c),r.push({url:c,skipped:!0,reason:"timeout",snippets:[]})):(console.warn("⚠️ Failed ("+(e&&e.message?e.message:e)+"): "+c),r.push({url:c,skipped:!0,reason:String(e&&e.message?e.message:e),snippets:[]}))}}console.groupEnd(),console.group("📄 Grepping inline scripts");for(var k=[],O=0;O<e.inlineScripts.length;O++)for(var D=e.inlineScripts[O].split("\n"),T=0;T<D.length;T++){for(var U=D[T],E=!1,F=0;F<o.length;F++)if(-1!==U.indexOf(o[F])){E=!0;break}if(E){for(var A=Math.max(0,T-2),j=Math.min(D.length-1,T+2),P=[],I=0;I<o.length;I++)-1!==U.indexOf(o[I])&&P.push(o[I]);k.push({scriptIndex:O,lineNumber:T+1,matchedKeywords:P,context:D.slice(A,j+1).join("\n")})}}console.log("Found "+k.length+" matches in inline scripts");for(var q=0;q<k.length;q++){var N=k[q];console.group("Inline #"+(N.scriptIndex+1)+" line "+N.lineNumber+" — ["+N.matchedKeywords.join(", ")+"]"),console.log(N.context),console.groupEnd()}console.groupEnd();var L={pageUrl:e.pageUrl,hostname:e.hostname,discoveredAt:e.discoveredAt,inlineScripts:e.inlineScripts,scriptUrls:e.scriptUrls,fileResults:r,inlineSnippets:k};e.error&&(L.error=e.error),console.group("📦 Full Enriched Payload");try{console.log(JSON.stringify(L,null,2))}catch(e){console.log(L)}return console.groupEnd(),console.log("✅ ttpDiscoverFull() complete"),console.groupEnd(),L}catch(e){try{console.error("[TTP Discovery] ttpDiscoverFull failed:",e),console.groupEnd()}catch(e){}return{pageUrl:function(){try{return window.location.href}catch(e){return""}}(),hostname:function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:(new Date).toISOString(),inlineScripts:[],scriptUrls:[],fileResults:[],inlineSnippets:[],error:String(e&&e.message?e.message:e)}}},function(){try{console.log("💡 TTP GO Discovery ready — run ttpDiscover() or ttpDiscoverFull() in console to start")}catch(e){}}()}();
@@ -192,19 +192,6 @@
192
192
  </div>
193
193
  </a>
194
194
 
195
- <a href="../examples/test-signed-link.html" class="test-card">
196
- <span class="test-card__icon">🔐</span>
197
- <h3 class="test-card__title">Signed Link Test</h3>
198
- <p class="test-card__description">
199
- Test secure authentication using signed links. Demonstrates how to use signed URLs
200
- for production-ready voice agent integration without exposing agent IDs.
201
- </p>
202
- <div class="test-card__badges">
203
- <span class="badge badge--voice">Voice</span>
204
- <span class="badge badge--sdk">Security</span>
205
- </div>
206
- </a>
207
-
208
195
  <a href="../examples/test.html" class="test-card">
209
196
  <span class="test-card__icon">⚡</span>
210
197
  <h3 class="test-card__title">Basic Voice Test</h3>
@@ -166,11 +166,11 @@
166
166
  </label>
167
167
  <label>STT Provider
168
168
  <select id="sttSelect">
169
+ <option value="soniox" selected>Soniox (default — no override)</option>
169
170
  <option value="">Default (server)</option>
170
- <option value="azure-stt" selected>Azure STT</option>
171
+ <option value="azure-stt">Azure STT</option>
171
172
  <option value="cartesia-stt">Cartesia (Ink-Whisper)</option>
172
173
  <option value="openai-realtime">OpenAI Realtime</option>
173
- <option value="soniox">Soniox</option>
174
174
  </select>
175
175
  </label>
176
176
  <label>Start Open
@@ -304,7 +304,8 @@ window.testWidget._flavor.messageHandlers['show_media']({
304
304
  agentSettingsOverride: (() => {
305
305
  const override = {};
306
306
  const stt = document.getElementById('sttSelect').value;
307
- if (stt) override.sttProvider = stt;
307
+ // Soniox is server default; only override when picking a non-default provider
308
+ if (stt && stt !== 'soniox') override.sttProvider = stt;
308
309
  return override;
309
310
  })(),
310
311
  behavior: {
@@ -316,7 +317,7 @@ window.testWidget._flavor.messageHandlers['show_media']({
316
317
  },
317
318
  icon: {
318
319
  type: 'custom',
319
- customImage: 'https://talktopc.com/logo192.png',
320
+ /* Omit customImage — SDK shows animated waveform (same as mobile), not the old default logo */
320
321
  size: 'medium',
321
322
  backgroundColor: '#FFFFFF'
322
323
  },
@@ -434,5 +435,6 @@ window.testWidget._flavor.messageHandlers['show_media']({
434
435
  updateStatus('SDK load failed: ' + e.message, 'error');
435
436
  });
436
437
  </script>
438
+ <script src="discovery.js"></script>
437
439
  </body>
438
440
  </html>
@@ -149,8 +149,9 @@
149
149
  </label>
150
150
  <label>STT Provider
151
151
  <select id="sttSelect">
152
+ <option value="soniox" selected>Soniox (default — no override)</option>
152
153
  <option value="">Default (server)</option>
153
- <option value="azure-stt" selected>Azure STT</option>
154
+ <option value="azure-stt">Azure STT</option>
154
155
  </select>
155
156
  </label>
156
157
  </div>
@@ -251,7 +252,8 @@ window.testWidget._flavor.messageHandlers['show_items']({
251
252
  agentSettingsOverride: (() => {
252
253
  const override = {};
253
254
  const stt = document.getElementById('sttSelect').value;
254
- if (stt) override.sttProvider = stt;
255
+ // Soniox is server default; only override when picking a non-default provider
256
+ if (stt && stt !== 'soniox') override.sttProvider = stt;
255
257
  return override;
256
258
  })(),
257
259
  behavior: {
@@ -264,7 +266,6 @@ window.testWidget._flavor.messageHandlers['show_items']({
264
266
  },
265
267
  icon: {
266
268
  type: 'custom',
267
- customImage: 'https://talktopc.com/logo192.png',
268
269
  size: 'medium',
269
270
  backgroundColor: '#FFFFFF'
270
271
  },
@@ -1366,7 +1366,6 @@
1366
1366
  },
1367
1367
  icon: {
1368
1368
  type: 'custom',
1369
- customImage: 'https://talktopc.com/logo192.png',
1370
1369
  size: 'medium',
1371
1370
  backgroundColor: '#FFFFFF'
1372
1371
  },
@@ -1538,7 +1537,7 @@
1538
1537
  const iconConfig = widgetConfig.icon;
1539
1538
  button.innerHTML = ''; // Clear previous content
1540
1539
 
1541
- if (iconConfig.type === 'custom' && iconConfig.customImage) {
1540
+ if (iconConfig.type === 'custom' && iconConfig.customImage != null && String(iconConfig.customImage).trim() !== '') {
1542
1541
  const img = document.createElement('img');
1543
1542
  img.src = iconConfig.customImage;
1544
1543
  img.alt = 'Chat Assistant';
@@ -1559,15 +1558,12 @@
1559
1558
  <path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
1560
1559
  </svg>`;
1561
1560
  } else {
1562
- // Fallback to custom image if type is custom but no image specified
1563
- const img = document.createElement('img');
1564
- img.src = 'https://talktopc.com/logo192.png';
1565
- img.alt = 'Chat Assistant';
1566
- const iconSize = Math.floor(getSizeValue(config.size) * 0.6);
1567
- img.style.width = iconSize + 'px';
1568
- img.style.height = iconSize + 'px';
1569
- img.style.objectFit = 'contain';
1570
- button.appendChild(img);
1561
+ /* custom with no URL (or other types): match SDK default mic icon for circle launcher */
1562
+ const iconSize = Math.floor(getSizeValue(config.size) * 0.5);
1563
+ button.innerHTML = `<svg viewBox="0 0 24 24" style="width: ${iconSize}px; height: ${iconSize}px; fill: #7C3AED;">
1564
+ <path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
1565
+ <path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
1566
+ </svg>`;
1571
1567
  }
1572
1568
 
1573
1569
  // Make button highlightable
@@ -2453,7 +2449,7 @@
2453
2449
  </div>
2454
2450
  <div class="control-item" id="iconCustomImageControl" style="display: ${widgetConfig.icon.type === 'custom' ? 'block' : 'none'};">
2455
2451
  <label>Image URL</label>
2456
- <input type="text" id="iconCustomImage" value="${widgetConfig.icon.customImage || 'https://talktopc.com/logo192.png'}" placeholder="https://talktopc.com/logo192.png">
2452
+ <input type="text" id="iconCustomImage" value="${widgetConfig.icon.customImage || ''}" placeholder="Optional image URL (empty = default icon)">
2457
2453
  </div>
2458
2454
  <div class="control-item" id="iconEmojiControl" style="display: ${widgetConfig.icon.type === 'emoji' ? 'block' : 'none'};">
2459
2455
  <label>Emoji</label>
@@ -3690,7 +3686,6 @@
3690
3686
  },
3691
3687
  icon: {
3692
3688
  type: 'custom',
3693
- customImage: 'https://talktopc.com/logo192.png',
3694
3689
  size: 'medium',
3695
3690
  backgroundColor: '#FFFFFF'
3696
3691
  },
@@ -4192,7 +4187,6 @@
4192
4187
  },
4193
4188
  icon: {
4194
4189
  type: 'custom',
4195
- customImage: 'https://talktopc.com/logo192.png',
4196
4190
  size: 'medium',
4197
4191
  backgroundColor: '#FFFFFF'
4198
4192
  },
@@ -816,6 +816,33 @@
816
816
  line-height: 1;
817
817
  }
818
818
 
819
+ /* Match SDK default pill: waveform in frosted circle */
820
+ .mock-pill-icon.mock-pill-icon--wave {
821
+ background: rgba(255, 255, 255, 0.15);
822
+ }
823
+ .mock-pill-waveform {
824
+ display: flex;
825
+ align-items: center;
826
+ gap: 2px;
827
+ height: 16px;
828
+ }
829
+ .mock-pill-waveform .bar {
830
+ width: 2px;
831
+ background: #fff;
832
+ border-radius: 1px;
833
+ animation: mockPillWaveAnim 0.8s ease-in-out infinite;
834
+ transform-origin: center bottom;
835
+ }
836
+ .mock-pill-waveform .bar:nth-child(1) { height: 5px; animation-delay: 0s; }
837
+ .mock-pill-waveform .bar:nth-child(2) { height: 10px; animation-delay: 0.1s; }
838
+ .mock-pill-waveform .bar:nth-child(3) { height: 16px; animation-delay: 0.2s; }
839
+ .mock-pill-waveform .bar:nth-child(4) { height: 8px; animation-delay: 0.3s; }
840
+ .mock-pill-waveform .bar:nth-child(5) { height: 12px; animation-delay: 0.4s; }
841
+ @keyframes mockPillWaveAnim {
842
+ 0%, 100% { transform: scaleY(0.4); }
843
+ 50% { transform: scaleY(1); }
844
+ }
845
+
819
846
  .mock-pill-text {
820
847
  display: flex;
821
848
  flex-direction: column;
@@ -1815,8 +1842,10 @@
1815
1842
  <div class="mock-prompt-bubble" id="mockPromptBubble" style="display: none;"></div>
1816
1843
  <div class="mock-pulse-rings" id="mockPulseRings" style="display: none;"></div>
1817
1844
  <button class="mock-pill-launcher" id="mockButton" data-element-type="button">
1818
- <div class="mock-pill-icon" id="mockPillIcon">
1819
- <img src="https://talktopc.com/logo192.png" alt="" style="width:22px;height:22px;border-radius:50%;object-fit:cover;">
1845
+ <div class="mock-pill-icon mock-pill-icon--wave" id="mockPillIcon">
1846
+ <div class="mock-pill-waveform" aria-hidden="true">
1847
+ <span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>
1848
+ </div>
1820
1849
  </div>
1821
1850
  <div class="mock-pill-text" id="mockPillText">
1822
1851
  <span class="mock-pill-title" id="mockPillTitle">Chat Assistant</span>
@@ -1977,7 +2006,6 @@
1977
2006
  },
1978
2007
  icon: {
1979
2008
  type: 'custom',
1980
- customImage: 'https://talktopc.com/logo192.png',
1981
2009
  size: 'medium',
1982
2010
  backgroundColor: '#FFFFFF'
1983
2011
  },
@@ -2175,17 +2203,23 @@
2175
2203
  const iconConfig = widgetConfig.icon;
2176
2204
  const headerConfig = widgetConfig.header;
2177
2205
 
2178
- // Update pill icon
2206
+ // Update pill icon (SDK: custom URL → image; else animated waveform like mobile FAB)
2179
2207
  const pillIcon = document.getElementById('mockPillIcon');
2180
2208
  if (pillIcon) {
2181
- if (iconConfig.type === 'custom' && iconConfig.customImage) {
2209
+ const hasCustomUrl = iconConfig.type === 'custom' && iconConfig.customImage != null && String(iconConfig.customImage).trim() !== '';
2210
+ if (hasCustomUrl) {
2211
+ pillIcon.className = 'mock-pill-icon';
2182
2212
  pillIcon.innerHTML = `<img src="${iconConfig.customImage}" alt="" style="width:22px;height:22px;border-radius:50%;object-fit:cover;">`;
2213
+ pillIcon.style.backgroundColor = iconConfig.backgroundColor || '#ffffff';
2183
2214
  } else if (iconConfig.type === 'emoji') {
2215
+ pillIcon.className = 'mock-pill-icon';
2184
2216
  pillIcon.innerHTML = `<span class="pill-emoji">${iconConfig.emoji || '🤖'}</span>`;
2217
+ pillIcon.style.backgroundColor = iconConfig.backgroundColor || '#ffffff';
2185
2218
  } else {
2186
- pillIcon.innerHTML = `<img src="${iconConfig.customImage || 'https://talktopc.com/logo192.png'}" alt="" style="width:22px;height:22px;border-radius:50%;object-fit:cover;">`;
2219
+ pillIcon.className = 'mock-pill-icon mock-pill-icon--wave';
2220
+ pillIcon.innerHTML = '<div class="mock-pill-waveform" aria-hidden="true"><span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span></div>';
2221
+ pillIcon.style.backgroundColor = '';
2187
2222
  }
2188
- pillIcon.style.backgroundColor = iconConfig.backgroundColor || '#ffffff';
2189
2223
  }
2190
2224
 
2191
2225
  // Update pill title and status
@@ -3179,7 +3213,7 @@
3179
3213
  </div>
3180
3214
  <div class="control-item" id="iconCustomImageControl" style="display: ${widgetConfig.icon.type === 'custom' ? 'block' : 'none'};">
3181
3215
  <label>Image URL</label>
3182
- <input type="text" id="iconCustomImage" value="${widgetConfig.icon.customImage || 'https://talktopc.com/logo192.png'}" placeholder="https://talktopc.com/logo192.png">
3216
+ <input type="text" id="iconCustomImage" value="${widgetConfig.icon.customImage || ''}" placeholder="Optional image URL — leave empty for waveform">
3183
3217
  </div>
3184
3218
  <div class="control-item" id="iconEmojiControl" style="display: ${widgetConfig.icon.type === 'emoji' ? 'block' : 'none'};">
3185
3219
  <label>Emoji</label>
@@ -4518,7 +4552,6 @@
4518
4552
  },
4519
4553
  icon: {
4520
4554
  type: 'custom',
4521
- customImage: 'https://talktopc.com/logo192.png',
4522
4555
  size: 'medium',
4523
4556
  backgroundColor: '#FFFFFF'
4524
4557
  },
@@ -5240,7 +5273,6 @@
5240
5273
  },
5241
5274
  icon: {
5242
5275
  type: 'custom',
5243
- customImage: 'https://talktopc.com/logo192.png',
5244
5276
  size: 'medium',
5245
5277
  backgroundColor: '#FFFFFF'
5246
5278
  },
@@ -0,0 +1 @@
1
+ !function(){"use strict";var e=["google.com","googleapis.com","gstatic.com","googletagmanager.com","google-analytics.com","googlesyndication.com","doubleclick.net","facebook.net","facebook.com","fbcdn.net","connect.facebook","cdn.jsdelivr.net","jsdelivr.net","unpkg.com","cdnjs.cloudflare.com","hotjar.com","segment.com","segment.io","mixpanel.com","clarity.ms","licdn.com","bat.bing.com","pinterest.com","tiktok.com","tiktokcdn.com","criteo.com","taboola.com","outbrain.com","cloudflareinsights.com","browser.sentry-cdn.com","sentry.io","amazon-adsystem.com","adsafeprotected.com","scorecardresearch.com","snapchat.com","twitter.com","twimg.com","x.com"];function o(e){if(!e||"string"!=typeof e)return!1;var o=e.trim();return!!o&&(-1!==o.indexOf("fetch")||-1!==o.indexOf("axios")||-1!==o.indexOf("ajax")||-1!==o.indexOf("api")||-1!==o.indexOf("search")||-1!==o.indexOf("query")||-1!==o.indexOf("חיפוש"))}function n(o,n){try{var r=new URL(o,window.location.href);if(function(o){if(!o)return!0;for(var n=String(o).toLowerCase(),r=0;r<e.length;r++)if(n===e[r]||n.endsWith("."+e[r]))return!0;return!1}(r.hostname))return!1;var t=window.location.hostname;if(!t)return!1;if(r.hostname===t)return!0;if(r.hostname.endsWith("."+t))return!0;if(-1!==o.indexOf(t))return!0;var c=n||"";return"/"===c.charAt(0)||0===c.indexOf("./")}catch(e){return!1}}function r(e){if(null==e)return"";var o=String(e);return o.length<=500?o:o.slice(0,500)+"…"}window.ttpDiscover=function(){var e="",t="",c=(new Date).toISOString(),i=[],s=[];try{e=window.location.href,t=window.location.hostname;var l=[];try{l=document.querySelectorAll("script:not([src])")}catch(e){l=[]}for(var a=0;a<l.length;a++)try{var u=(l[a].textContent||"").trim();if(!o(u))continue;i.push(u)}catch(e){}var p=[];try{p=document.querySelectorAll("script[src]")}catch(e){p=[]}for(var g={},d=0;d<p.length;d++)try{var h=p[d],m=h.getAttribute("src")||"",f=h.src||"";if(!n(f,m))continue;if(g[f])continue;g[f]=!0,s.push(f)}catch(e){}}catch(o){return console.error("[TTP Discovery] Unexpected error while scanning:",o),{pageUrl:e||function(){try{return window.location.href}catch(e){return""}}(),hostname:t||function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:c,inlineScripts:[],scriptUrls:[],error:String(o&&o.message?o.message:o)}}var v={pageUrl:e,hostname:t,discoveredAt:c,inlineScripts:i,scriptUrls:s};try{console.group("🔍 TTP GO Site Discovery"),console.log("Starting discovery on: "+e),console.group("📄 Inline Scripts"),console.log("Found "+i.length+" relevant inline scripts");for(var y=0;y<i.length;y++)console.log("--- Inline Script #"+(y+1)+" ---"),console.log(r(i[y]));console.groupEnd(),console.group("🔗 Script URLs"),console.log("Found "+s.length+" same-domain script files");for(var w=0;w<s.length;w++)console.log(" - "+s[w]);console.groupEnd(),console.group("📦 Full Payload (ready to send to backend)");try{console.log(JSON.stringify(v,null,2))}catch(e){console.log(v)}console.groupEnd(),console.log("✅ Discovery complete. Call ttpDiscover() again anytime."),console.groupEnd()}catch(e){try{console.log(v)}catch(e){}}return v},window.ttpDiscoverFull=async function(){try{console.group("🚀 TTP GO Discovery Full");var e=await Promise.resolve(window.ttpDiscover()),o=["fetch(","axios.","$.ajax","XMLHttpRequest","/api/","/search","/products","?q=","?search=","?s=","search=","query=","חיפוש"],n=5e5,r=[];console.group("📂 Fetching JS files");for(var t=0;t<e.scriptUrls.length;t++){var c=e.scriptUrls[t];console.log("Fetching:",c);try{var i,s=new AbortController,l=setTimeout(function(){s.abort()},5e3);try{i=await fetch(c,{signal:s.signal})}finally{clearTimeout(l)}if(!i.ok){console.warn("⚠️ HTTP "+i.status+": "+c),r.push({url:c,skipped:!0,reason:"http_"+i.status,snippets:[]});continue}var a=i.headers.get("content-length");if(a&&parseInt(a,10)>n){console.warn("⚠️ Skipped (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}var u=await i.text();if(u.length>n){console.warn("⚠️ Skipped after read (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}for(var p=u.split("\n"),g=[],d=0;d<p.length;d++){for(var h=p[d],m=!1,f=0;f<o.length;f++)if(-1!==h.indexOf(o[f])){m=!0;break}if(m){for(var v=Math.max(0,d-2),y=Math.min(p.length-1,d+2),w=[],x=0;x<o.length;x++)-1!==h.indexOf(o[x])&&w.push(o[x]);g.push({lineNumber:d+1,matchedKeywords:w,context:p.slice(v,y+1).join("\n")})}}console.log(" → "+g.length+" matches found");for(var S=0;S<g.length;S++){var b=g[S];console.group(" Line "+b.lineNumber+" — ["+b.matchedKeywords.join(", ")+"]"),console.log(b.context),console.groupEnd()}r.push({url:c,skipped:!1,snippets:g})}catch(e){e&&"AbortError"===e.name?(console.warn("⚠️ Timeout: "+c),r.push({url:c,skipped:!0,reason:"timeout",snippets:[]})):(console.warn("⚠️ Failed ("+(e&&e.message?e.message:e)+"): "+c),r.push({url:c,skipped:!0,reason:String(e&&e.message?e.message:e),snippets:[]}))}}console.groupEnd(),console.group("📄 Grepping inline scripts");for(var k=[],O=0;O<e.inlineScripts.length;O++)for(var D=e.inlineScripts[O].split("\n"),T=0;T<D.length;T++){for(var U=D[T],E=!1,F=0;F<o.length;F++)if(-1!==U.indexOf(o[F])){E=!0;break}if(E){for(var A=Math.max(0,T-2),j=Math.min(D.length-1,T+2),P=[],I=0;I<o.length;I++)-1!==U.indexOf(o[I])&&P.push(o[I]);k.push({scriptIndex:O,lineNumber:T+1,matchedKeywords:P,context:D.slice(A,j+1).join("\n")})}}console.log("Found "+k.length+" matches in inline scripts");for(var q=0;q<k.length;q++){var N=k[q];console.group("Inline #"+(N.scriptIndex+1)+" line "+N.lineNumber+" — ["+N.matchedKeywords.join(", ")+"]"),console.log(N.context),console.groupEnd()}console.groupEnd();var L={pageUrl:e.pageUrl,hostname:e.hostname,discoveredAt:e.discoveredAt,inlineScripts:e.inlineScripts,scriptUrls:e.scriptUrls,fileResults:r,inlineSnippets:k};e.error&&(L.error=e.error),console.group("📦 Full Enriched Payload");try{console.log(JSON.stringify(L,null,2))}catch(e){console.log(L)}return console.groupEnd(),console.log("✅ ttpDiscoverFull() complete"),console.groupEnd(),L}catch(e){try{console.error("[TTP Discovery] ttpDiscoverFull failed:",e),console.groupEnd()}catch(e){}return{pageUrl:function(){try{return window.location.href}catch(e){return""}}(),hostname:function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:(new Date).toISOString(),inlineScripts:[],scriptUrls:[],fileResults:[],inlineSnippets:[],error:String(e&&e.message?e.message:e)}}},function(){try{console.log("💡 TTP GO Discovery ready — run ttpDiscover() or ttpDiscoverFull() in console to start")}catch(e){}}()}();
@@ -721,9 +721,9 @@
721
721
 
722
722
  <option value="22050">22.05 kHz</option>
723
723
 
724
- <option value="24000">24 kHz</option>
724
+ <option value="24000" selected>24 kHz</option>
725
725
 
726
- <option value="44100" selected>44.1 kHz (CD Quality)</option>
726
+ <option value="44100">44.1 kHz (CD Quality)</option>
727
727
 
728
728
  <option value="48000">48 kHz (Pro)</option>
729
729
 
@@ -0,0 +1 @@
1
+ !function(){"use strict";var e=["google.com","googleapis.com","gstatic.com","googletagmanager.com","google-analytics.com","googlesyndication.com","doubleclick.net","facebook.net","facebook.com","fbcdn.net","connect.facebook","cdn.jsdelivr.net","jsdelivr.net","unpkg.com","cdnjs.cloudflare.com","hotjar.com","segment.com","segment.io","mixpanel.com","clarity.ms","licdn.com","bat.bing.com","pinterest.com","tiktok.com","tiktokcdn.com","criteo.com","taboola.com","outbrain.com","cloudflareinsights.com","browser.sentry-cdn.com","sentry.io","amazon-adsystem.com","adsafeprotected.com","scorecardresearch.com","snapchat.com","twitter.com","twimg.com","x.com"];function o(e){if(!e||"string"!=typeof e)return!1;var o=e.trim();return!!o&&(-1!==o.indexOf("fetch")||-1!==o.indexOf("axios")||-1!==o.indexOf("ajax")||-1!==o.indexOf("api")||-1!==o.indexOf("search")||-1!==o.indexOf("query")||-1!==o.indexOf("חיפוש"))}function n(o,n){try{var r=new URL(o,window.location.href);if(function(o){if(!o)return!0;for(var n=String(o).toLowerCase(),r=0;r<e.length;r++)if(n===e[r]||n.endsWith("."+e[r]))return!0;return!1}(r.hostname))return!1;var t=window.location.hostname;if(!t)return!1;if(r.hostname===t)return!0;if(r.hostname.endsWith("."+t))return!0;if(-1!==o.indexOf(t))return!0;var c=n||"";return"/"===c.charAt(0)||0===c.indexOf("./")}catch(e){return!1}}function r(e){if(null==e)return"";var o=String(e);return o.length<=500?o:o.slice(0,500)+"…"}window.ttpDiscover=function(){var e="",t="",c=(new Date).toISOString(),i=[],s=[];try{e=window.location.href,t=window.location.hostname;var l=[];try{l=document.querySelectorAll("script:not([src])")}catch(e){l=[]}for(var a=0;a<l.length;a++)try{var u=(l[a].textContent||"").trim();if(!o(u))continue;i.push(u)}catch(e){}var p=[];try{p=document.querySelectorAll("script[src]")}catch(e){p=[]}for(var g={},d=0;d<p.length;d++)try{var h=p[d],m=h.getAttribute("src")||"",f=h.src||"";if(!n(f,m))continue;if(g[f])continue;g[f]=!0,s.push(f)}catch(e){}}catch(o){return console.error("[TTP Discovery] Unexpected error while scanning:",o),{pageUrl:e||function(){try{return window.location.href}catch(e){return""}}(),hostname:t||function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:c,inlineScripts:[],scriptUrls:[],error:String(o&&o.message?o.message:o)}}var v={pageUrl:e,hostname:t,discoveredAt:c,inlineScripts:i,scriptUrls:s};try{console.group("🔍 TTP GO Site Discovery"),console.log("Starting discovery on: "+e),console.group("📄 Inline Scripts"),console.log("Found "+i.length+" relevant inline scripts");for(var y=0;y<i.length;y++)console.log("--- Inline Script #"+(y+1)+" ---"),console.log(r(i[y]));console.groupEnd(),console.group("🔗 Script URLs"),console.log("Found "+s.length+" same-domain script files");for(var w=0;w<s.length;w++)console.log(" - "+s[w]);console.groupEnd(),console.group("📦 Full Payload (ready to send to backend)");try{console.log(JSON.stringify(v,null,2))}catch(e){console.log(v)}console.groupEnd(),console.log("✅ Discovery complete. Call ttpDiscover() again anytime."),console.groupEnd()}catch(e){try{console.log(v)}catch(e){}}return v},window.ttpDiscoverFull=async function(){try{console.group("🚀 TTP GO Discovery Full");var e=await Promise.resolve(window.ttpDiscover()),o=["fetch(","axios.","$.ajax","XMLHttpRequest","/api/","/search","/products","?q=","?search=","?s=","search=","query=","חיפוש"],n=5e5,r=[];console.group("📂 Fetching JS files");for(var t=0;t<e.scriptUrls.length;t++){var c=e.scriptUrls[t];console.log("Fetching:",c);try{var i,s=new AbortController,l=setTimeout(function(){s.abort()},5e3);try{i=await fetch(c,{signal:s.signal})}finally{clearTimeout(l)}if(!i.ok){console.warn("⚠️ HTTP "+i.status+": "+c),r.push({url:c,skipped:!0,reason:"http_"+i.status,snippets:[]});continue}var a=i.headers.get("content-length");if(a&&parseInt(a,10)>n){console.warn("⚠️ Skipped (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}var u=await i.text();if(u.length>n){console.warn("⚠️ Skipped after read (too large): "+c),r.push({url:c,skipped:!0,reason:"too_large",snippets:[]});continue}for(var p=u.split("\n"),g=[],d=0;d<p.length;d++){for(var h=p[d],m=!1,f=0;f<o.length;f++)if(-1!==h.indexOf(o[f])){m=!0;break}if(m){for(var v=Math.max(0,d-2),y=Math.min(p.length-1,d+2),w=[],x=0;x<o.length;x++)-1!==h.indexOf(o[x])&&w.push(o[x]);g.push({lineNumber:d+1,matchedKeywords:w,context:p.slice(v,y+1).join("\n")})}}console.log(" → "+g.length+" matches found");for(var S=0;S<g.length;S++){var b=g[S];console.group(" Line "+b.lineNumber+" — ["+b.matchedKeywords.join(", ")+"]"),console.log(b.context),console.groupEnd()}r.push({url:c,skipped:!1,snippets:g})}catch(e){e&&"AbortError"===e.name?(console.warn("⚠️ Timeout: "+c),r.push({url:c,skipped:!0,reason:"timeout",snippets:[]})):(console.warn("⚠️ Failed ("+(e&&e.message?e.message:e)+"): "+c),r.push({url:c,skipped:!0,reason:String(e&&e.message?e.message:e),snippets:[]}))}}console.groupEnd(),console.group("📄 Grepping inline scripts");for(var k=[],O=0;O<e.inlineScripts.length;O++)for(var D=e.inlineScripts[O].split("\n"),T=0;T<D.length;T++){for(var U=D[T],E=!1,F=0;F<o.length;F++)if(-1!==U.indexOf(o[F])){E=!0;break}if(E){for(var A=Math.max(0,T-2),j=Math.min(D.length-1,T+2),P=[],I=0;I<o.length;I++)-1!==U.indexOf(o[I])&&P.push(o[I]);k.push({scriptIndex:O,lineNumber:T+1,matchedKeywords:P,context:D.slice(A,j+1).join("\n")})}}console.log("Found "+k.length+" matches in inline scripts");for(var q=0;q<k.length;q++){var N=k[q];console.group("Inline #"+(N.scriptIndex+1)+" line "+N.lineNumber+" — ["+N.matchedKeywords.join(", ")+"]"),console.log(N.context),console.groupEnd()}console.groupEnd();var L={pageUrl:e.pageUrl,hostname:e.hostname,discoveredAt:e.discoveredAt,inlineScripts:e.inlineScripts,scriptUrls:e.scriptUrls,fileResults:r,inlineSnippets:k};e.error&&(L.error=e.error),console.group("📦 Full Enriched Payload");try{console.log(JSON.stringify(L,null,2))}catch(e){console.log(L)}return console.groupEnd(),console.log("✅ ttpDiscoverFull() complete"),console.groupEnd(),L}catch(e){try{console.error("[TTP Discovery] ttpDiscoverFull failed:",e),console.groupEnd()}catch(e){}return{pageUrl:function(){try{return window.location.href}catch(e){return""}}(),hostname:function(){try{return window.location.hostname}catch(e){return""}}(),discoveredAt:(new Date).toISOString(),inlineScripts:[],scriptUrls:[],fileResults:[],inlineSnippets:[],error:String(e&&e.message?e.message:e)}}},function(){try{console.log("💡 TTP GO Discovery ready — run ttpDiscover() or ttpDiscoverFull() in console to start")}catch(e){}}()}();
@@ -166,11 +166,11 @@
166
166
  </label>
167
167
  <label>STT Provider
168
168
  <select id="sttSelect">
169
+ <option value="soniox" selected>Soniox (default — no override)</option>
169
170
  <option value="">Default (server)</option>
170
- <option value="azure-stt" selected>Azure STT</option>
171
+ <option value="azure-stt">Azure STT</option>
171
172
  <option value="cartesia-stt">Cartesia (Ink-Whisper)</option>
172
173
  <option value="openai-realtime">OpenAI Realtime</option>
173
- <option value="soniox">Soniox</option>
174
174
  </select>
175
175
  </label>
176
176
  <label>Start Open
@@ -304,7 +304,8 @@ window.testWidget._flavor.messageHandlers['show_media']({
304
304
  agentSettingsOverride: (() => {
305
305
  const override = {};
306
306
  const stt = document.getElementById('sttSelect').value;
307
- if (stt) override.sttProvider = stt;
307
+ // Soniox is server default; only override when picking a non-default provider
308
+ if (stt && stt !== 'soniox') override.sttProvider = stt;
308
309
  return override;
309
310
  })(),
310
311
  behavior: {
@@ -316,7 +317,7 @@ window.testWidget._flavor.messageHandlers['show_media']({
316
317
  },
317
318
  icon: {
318
319
  type: 'custom',
319
- customImage: 'https://talktopc.com/logo192.png',
320
+ /* Omit customImage — SDK shows animated waveform (same as mobile), not the old default logo */
320
321
  size: 'medium',
321
322
  backgroundColor: '#FFFFFF'
322
323
  },
@@ -434,5 +435,6 @@ window.testWidget._flavor.messageHandlers['show_media']({
434
435
  updateStatus('SDK load failed: ' + e.message, 'error');
435
436
  });
436
437
  </script>
438
+ <script src="discovery.js"></script>
437
439
  </body>
438
440
  </html>
@@ -192,19 +192,6 @@
192
192
  </div>
193
193
  </a>
194
194
 
195
- <a href="../examples/test-signed-link.html" class="test-card">
196
- <span class="test-card__icon">🔐</span>
197
- <h3 class="test-card__title">Signed Link Test</h3>
198
- <p class="test-card__description">
199
- Test secure authentication using signed links. Demonstrates how to use signed URLs
200
- for production-ready voice agent integration without exposing agent IDs.
201
- </p>
202
- <div class="test-card__badges">
203
- <span class="badge badge--voice">Voice</span>
204
- <span class="badge badge--sdk">Security</span>
205
- </div>
206
- </a>
207
-
208
195
  <a href="../examples/test.html" class="test-card">
209
196
  <span class="test-card__icon">⚡</span>
210
197
  <h3 class="test-card__title">Basic Voice Test</h3>
@@ -127,7 +127,7 @@
127
127
  <li>Uses <code>TTPChatWidget</code> with <code>flavor: { type: 'restaurants', partnerId: 'mock-restaurant' }</code></li>
128
128
  <li>The backend injects restaurant tools (<code>search_menu</code>, <code>add_to_order</code>, <code>get_order</code>, <code>show_media</code>)</li>
129
129
  <li>Menu item cards display when the agent sends <code>show_items</code> messages</li>
130
- <li>Order summary updates on <code>order_updated</code> messages</li>
130
+ <li>Order summary updates on <code>cart_updated</code> messages (same universal shape as ecommerce: <code>product</code>, <code>cartTotal</code>, <code>cartItemCount</code>)</li>
131
131
  <li>Gallery (<code>show_media</code>) opens fullscreen for dish photos, ambiance, etc.</li>
132
132
  </ul>
133
133
  </div>
@@ -149,8 +149,9 @@
149
149
  </label>
150
150
  <label>STT Provider
151
151
  <select id="sttSelect">
152
+ <option value="soniox" selected>Soniox (default — no override)</option>
152
153
  <option value="">Default (server)</option>
153
- <option value="azure-stt" selected>Azure STT</option>
154
+ <option value="azure-stt">Azure STT</option>
154
155
  </select>
155
156
  </label>
156
157
  </div>
@@ -251,7 +252,8 @@ window.testWidget._flavor.messageHandlers['show_items']({
251
252
  agentSettingsOverride: (() => {
252
253
  const override = {};
253
254
  const stt = document.getElementById('sttSelect').value;
254
- if (stt) override.sttProvider = stt;
255
+ // Soniox is server default; only override when picking a non-default provider
256
+ if (stt && stt !== 'soniox') override.sttProvider = stt;
255
257
  return override;
256
258
  })(),
257
259
  behavior: {
@@ -259,19 +261,18 @@ window.testWidget._flavor.messageHandlers['show_items']({
259
261
  startOpen: true
260
262
  },
261
263
  flavor: {
262
- type: 'ecommerce',
263
- partnerId: 'mock-store'
264
+ type: 'restaurants',
265
+ partnerId: 'mock-restaurant'
264
266
  },
265
267
  icon: {
266
268
  type: 'custom',
267
- customImage: 'https://talktopc.com/logo192.png',
268
269
  size: 'medium',
269
270
  backgroundColor: '#FFFFFF'
270
271
  }
271
272
  });
272
273
 
273
274
  window.testWidget = chatWidget;
274
- updateStatus('TTPChatWidget created — ecommerce flavor (mock-store)', 'success');
275
+ updateStatus('TTPChatWidget created — restaurants flavor (mock-restaurant)', 'success');
275
276
  }
276
277
 
277
278
  document.getElementById('createBtn').onclick = createWidget;
@@ -325,14 +326,15 @@ window.testWidget._flavor.messageHandlers['show_items']({
325
326
  updateStatus('Create widget first', 'error');
326
327
  return;
327
328
  }
328
- chatWidget._flavor.messageHandlers['order_updated']({
329
- t: 'order_updated',
330
- orderItemCount: 3,
331
- orderTotal: 67.97,
329
+ chatWidget._flavor.messageHandlers['cart_updated']({
330
+ t: 'cart_updated',
331
+ cartItemCount: 3,
332
+ cartTotal: 67.97,
333
+ currency: 'USD',
332
334
  action: 'added',
333
- item: { id: 'MAIN-001', name: 'Grilled Salmon', price: 24.99 }
335
+ product: { id: 'MAIN-001', name: 'Grilled Salmon', price: 24.99 }
334
336
  });
335
- updateStatus('order_updated injected (3 items, $67.97)', 'success');
337
+ updateStatus('cart_updated injected (3 items, $67.97)', 'success');
336
338
  };
337
339
 
338
340
  document.getElementById('clearOrderBtn').onclick = () => {
@@ -340,10 +342,11 @@ window.testWidget._flavor.messageHandlers['show_items']({
340
342
  updateStatus('Create widget first', 'error');
341
343
  return;
342
344
  }
343
- chatWidget._flavor.messageHandlers['order_updated']({
344
- t: 'order_updated',
345
- orderItemCount: 0,
346
- orderTotal: 0,
345
+ chatWidget._flavor.messageHandlers['cart_updated']({
346
+ t: 'cart_updated',
347
+ cartItemCount: 0,
348
+ cartTotal: 0,
349
+ currency: 'USD',
347
350
  action: 'refreshed'
348
351
  });
349
352
  updateStatus('Order cleared', '');
@@ -94,11 +94,15 @@
94
94
  font-size: 13px;
95
95
  }
96
96
 
97
- select, input[type="text"], input[type="number"], input[type="color"] {
97
+ select, input[type="text"], input[type="number"], input[type="color"], textarea {
98
98
  font-size: 16px; /* Prevents iOS zoom on focus */
99
99
  padding: 10px 12px;
100
100
  min-height: 44px;
101
101
  }
102
+
103
+ textarea {
104
+ min-height: 100px;
105
+ }
102
106
 
103
107
  input[type="color"] {
104
108
  min-width: 50px;
@@ -188,7 +192,8 @@
188
192
  background: #4B5563;
189
193
  }
190
194
 
191
- label {
195
+ /* Scope form labels — global `label { flex-direction: column }` can break in-widget flex rows when useShadowDOM is false */
196
+ .customization-container label {
192
197
  display: flex;
193
198
  flex-direction: column;
194
199
  gap: 4px;
@@ -196,7 +201,7 @@
196
201
  color: #374151;
197
202
  }
198
203
 
199
- select, input[type="text"], input[type="number"], input[type="color"] {
204
+ select, input[type="text"], input[type="number"], input[type="color"], textarea {
200
205
  padding: 8px 12px;
201
206
  border: 1px solid #D1D5DB;
202
207
  border-radius: 6px;
@@ -206,7 +211,12 @@
206
211
  color: #111827;
207
212
  }
208
213
 
209
- select:focus, input:focus {
214
+ textarea {
215
+ resize: vertical;
216
+ min-height: 80px;
217
+ }
218
+
219
+ select:focus, input:focus, textarea:focus {
210
220
  outline: none;
211
221
  border-color: #667eea;
212
222
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
@@ -844,7 +854,7 @@
844
854
  } else {
845
855
  initializeUI();
846
856
  }
847
-
857
+
848
858
  // Clear hover color (set to empty to test no hover color behavior)
849
859
  function clearHoverColor() {
850
860
  const hoverColorInput = document.getElementById('buttonHoverColor');
@@ -931,7 +941,6 @@
931
941
 
932
942
  icon: {
933
943
  type: 'custom',
934
- customImage: 'https://talktopc.com/logo192.png',
935
944
  size: document.getElementById('buttonSize').value,
936
945
  backgroundColor: document.getElementById('buttonBgColor').value || '#FFFFFF' // Widget default: white
937
946
  },
@@ -1050,7 +1059,7 @@
1050
1059
  textCardIcon: document.getElementById('landingTextIcon').value
1051
1060
  }
1052
1061
  };
1053
-
1062
+
1054
1063
  // Only include hoverColor if user has set a custom value (not default #7C3AED)
1055
1064
  // If hoverColor is not included, widget will keep original color on hover
1056
1065
  const hoverColorInput = document.getElementById('buttonHoverColor');
@@ -1098,8 +1107,8 @@
1098
1107
  // Skip if already has listener
1099
1108
  if (elementsWithListeners.has(element)) return;
1100
1109
 
1101
- // For text inputs - apply on every keystroke (input event) with debounce
1102
- if (element.type === 'text' || element.tagName === 'TEXTAREA') {
1110
+ // For text inputs and textareas - apply on every keystroke (input event) with debounce
1111
+ if (element.type === 'text' || element.type === 'textarea' || element.tagName === 'TEXTAREA') {
1103
1112
  let timeout;
1104
1113
  element.addEventListener('input', function() {
1105
1114
  // Debounce for text inputs to avoid too many updates
@@ -149,8 +149,9 @@
149
149
  </label>
150
150
  <label>STT Provider
151
151
  <select id="sttSelect">
152
+ <option value="soniox" selected>Soniox (default — no override)</option>
152
153
  <option value="">Default (server)</option>
153
- <option value="azure-stt" selected>Azure STT</option>
154
+ <option value="azure-stt">Azure STT</option>
154
155
  </select>
155
156
  </label>
156
157
  </div>
@@ -251,7 +252,8 @@ window.testWidget._flavor.messageHandlers['show_items']({
251
252
  agentSettingsOverride: (() => {
252
253
  const override = {};
253
254
  const stt = document.getElementById('sttSelect').value;
254
- if (stt) override.sttProvider = stt;
255
+ // Soniox is server default; only override when picking a non-default provider
256
+ if (stt && stt !== 'soniox') override.sttProvider = stt;
255
257
  return override;
256
258
  })(),
257
259
  behavior: {
@@ -264,7 +266,6 @@ window.testWidget._flavor.messageHandlers['show_items']({
264
266
  },
265
267
  icon: {
266
268
  type: 'custom',
267
- customImage: 'https://talktopc.com/logo192.png',
268
269
  size: 'medium',
269
270
  backgroundColor: '#FFFFFF'
270
271
  },