ttp-agent-sdk 2.48.12 → 2.48.16
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 +212 -29
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.js +1 -1
- package/dist/audio-processor.js +41 -3
- package/dist/index.html +1 -1
- package/package.json +1 -1
package/dist/agent-widget.dev.js
CHANGED
|
@@ -9170,7 +9170,7 @@ if (false) // removed by dead control flow
|
|
|
9170
9170
|
/***/ ((module) => {
|
|
9171
9171
|
|
|
9172
9172
|
"use strict";
|
|
9173
|
-
module.exports = "/**\n * AudioProcessor - AudioWorklet for real-time audio processing\n * \n * This AudioWorklet processes audio data in real-time and sends it to the main thread\n * for transmission to the WebSocket server.\n */\n\nclass AudioProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n \n // Configuration\n this.config = options.processorOptions || {};\n // Use AudioContext sampleRate (available as global 'sampleRate' in AudioWorkletProcessor)\n // Fall back to config if sampleRate not available, default to 24 kHz to match typical server/TTS output\n this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 24000);\n this.bufferSize = 128; // Process 128 samples at a time (256 bytes = 8ms at 16kHz)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n \n // VAD (Voice Activity Detection) parameters\n this.silenceThreshold = 0.02; // RMS threshold for voice detection (increased to reduce ambient noise sensitivity)\n this.VOICE_FRAMES_REQUIRED = 2; // Require 2 consecutive frames above threshold before activating\n this.minVoiceDuration = 100; // ms - minimum speech duration\n this.pauseThreshold = 3000; // ms - longer pause before processing\n \n // VAD state\n this.isVoiceActive = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.silenceFramesThreshold = 5; // More frames needed for silence detection\n this.voiceFrameCount = 0; // Count consecutive frames above threshold\n \n // Audio quality tracking\n this.frameCount = 0;\n this.lastLogTime = 0;\n \n // Continuous recording mode\n this.continuousMode = true; // Always send audio when voice is detected\n this.forceContinuous = true; // Force continuous for toggle button behavior\n this.isCurrentlyStreaming = false; // Track if we're currently sending audio\n \n // Batching buffer\n this.sendBuffer = null;\n this.sendBufferBytes = 0;\n\n // Start true so the first process() call cannot terminate the worklet before\n // the main thread posts setForceContinuous (WebKit/iOS often renders audio before port messages are handled).\n this.isProcessing = true;\n \n // Handle messages from main thread\n this.port.onmessage = (event) => {\n const { type, data } = event.data;\n \n switch (type) {\n case 'start':\n this.isProcessing = true;\n this.isCurrentlyStreaming = true;\n break;\n \n case 'stop':\n this.isProcessing = false;\n this.isCurrentlyStreaming = false;\n this.isVoiceActive = false;\n this.forceContinuous = false;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Flush any remaining data\n this.flushBuffer();\n break;\n \n case 'setForceContinuous':\n this.forceContinuous = data.enabled;\n this.isProcessing = true;\n // iOS: bypass client VAD - always send. Desktop: keep VAD (saves bandwidth, reduces noise).\n this.isCurrentlyStreaming = data.enabled && (data.bypassVad === true);\n break;\n \n case 'flush':\n this.flushBuffer();\n break;\n \n case 'config':\n Object.assign(this.config, data);\n break;\n }\n };\n }\n \n /**\n * Process audio data\n */\n process(inputs, outputs, parameters) {\n // CRITICAL: If processing is stopped, terminate the processor\n if (!this.isProcessing) {\n // Return false to terminate the AudioWorklet processor\n // This stops all VAD processing immediately\n return false;\n }\n \n const input = inputs[0];\n const output = outputs[0];\n \n // Copy input to output (pass-through)\n if (input.length > 0 && output.length > 0) {\n output[0].set(input[0]);\n }\n \n // Process audio for PCM recording and VAD\n if (input.length > 0 && input[0].length > 0) {\n this.processAudioData(input[0]);\n }\n \n // Keep the processor alive\n return true;\n }\n \n processAudioData(audioData) {\n // CRITICAL: Early return if processing is stopped\n // This prevents VAD calculations and logging when stopped\n if (!this.isProcessing) {\n return;\n }\n \n this.frameCount++;\n \n // Process audio in consistent 128-sample chunks (256 bytes)\n for (let i = 0; i < audioData.length; i += this.bufferSize) {\n const chunkSize = Math.min(this.bufferSize, audioData.length - i);\n \n // Copy chunk to buffer\n for (let j = 0; j < chunkSize; j++) {\n this.buffer[j] = audioData[i + j];\n }\n \n // Pad with zeros if needed\n for (let j = chunkSize; j < this.bufferSize; j++) {\n this.buffer[j] = 0;\n }\n \n // Calculate RMS for VAD on this chunk\n let sum = 0;\n for (let j = 0; j < this.bufferSize; j++) {\n sum += this.buffer[j] * this.buffer[j];\n }\n const rms = Math.sqrt(sum / this.bufferSize);\n \n // Calculate additional features for better VAD\n let variation = 0;\n let highFreqCount = 0;\n for (let j = 1; j < this.bufferSize; j++) {\n const diff = Math.abs(this.buffer[j] - this.buffer[j-1]);\n variation += diff;\n if (diff > 0.1) highFreqCount++;\n }\n variation = variation / this.bufferSize;\n const highFreqRatio = highFreqCount / this.bufferSize;\n \n const currentTime = Date.now();\n \n // VAD with reduced sensitivity - require consecutive frames above threshold\n let hasVoice = rms > this.silenceThreshold;\n // Calculate time since last voice detection\n const timeSinceLastVoice = currentTime - this.lastVoiceTime;\n\n // Voice detection logic - require consecutive frames above threshold\n if (hasVoice) {\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount++; // Increment consecutive voice frame count\n\n // Only activate streaming after required consecutive frames\n if (this.voiceFrameCount >= this.VOICE_FRAMES_REQUIRED) {\n // Start voice if needed\n if (!this.isVoiceActive) {\n this.isVoiceActive = true;\n this.voiceStartTime = currentTime;\n this.isCurrentlyStreaming = true;\n // Log voice detection (every 50 frames = ~400ms to avoid spam)\n if (this.frameCount % 50 === 0) {\n console.log(`🎤 VAD: VOICE DETECTED (RMS: ${rms.toFixed(4)}, frames: ${this.voiceFrameCount})`);\n }\n }\n }\n\n this.lastVoiceTime = currentTime;\n } else {\n // Silence detected - reset voice frame count\n this.voiceFrameCount = 0;\n this.consecutiveSilenceFrames++;\n \n // In continuous mode, we still use VAD but require longer silence before stopping\n // In non-continuous mode, stop quickly\n const silenceThreshold = this.forceContinuous ? 3000 : 200; // 3s for continuous, 200ms otherwise\n \n // FIXED: Stop condition - also check isCurrentlyStreaming (not just isVoiceActive)\n // This handles case where setForceContinuous set streaming=true but no voice was detected yet\n if (!hasVoice && (this.isVoiceActive || this.isCurrentlyStreaming) && timeSinceLastVoice >= silenceThreshold) {\n this.isVoiceActive = false;\n this.isCurrentlyStreaming = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Log silence detection\n console.log(`🔇 VAD: SILENCE DETECTED (${timeSinceLastVoice}ms silence, RMS: ${rms.toFixed(4)})`);\n }\n }\n\n // Send PCM **only if streaming and processing** - hard gate\n // This ensures we only send audio when voice is detected, even in continuous mode\n if (this.isCurrentlyStreaming && this.isProcessing) {\n // Log occasionally when sending (every 200 frames = ~1.6 seconds to avoid spam)\n if (this.frameCount % 200 === 0) {\n console.log(`📤 VAD: Sending audio (isVoiceActive: ${this.isVoiceActive}, RMS: ${rms.toFixed(4)})`);\n }\n this.sendPCMAudioData(this.buffer);\n } else {\n // Log occasionally when blocking (every 200 frames)\n if (this.frameCount % 200 === 0 && this.isProcessing) {\n console.log(`🚫 VAD: Blocking audio (isCurrentlyStreaming: ${this.isCurrentlyStreaming}, RMS: ${rms.toFixed(4)})`);\n }\n }\n }\n }\n \n sendPCMAudioData(float32Data) {\n // Convert Float32Array (-1.0 to 1.0) to Int16Array (-32768 to 32767)\n const pcmData = new Int16Array(float32Data.length);\n \n for (let i = 0; i < float32Data.length; i++) {\n // Clamp and convert to 16-bit PCM\n const sample = Math.max(-1.0, Math.min(1.0, float32Data[i]));\n pcmData[i] = Math.round(sample * 32767);\n }\n \n // Initialize send buffer if not exists\n if (!this.sendBuffer) {\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n \n // Accumulate chunks in buffer\n this.sendBuffer.push(pcmData);\n this.sendBufferBytes += pcmData.byteLength;\n \n // Send in ~4 KB batches (≈128 ms of audio at 16kHz)\n // Use sliding window approach to maintain continuous flow\n while (this.sendBufferBytes >= 4096) {\n // Calculate how many chunks we need for ~4KB\n let chunksToSend = 0;\n let bytesToSend = 0;\n \n for (let i = 0; i < this.sendBuffer.length; i++) {\n const chunkBytes = this.sendBuffer[i].byteLength;\n if (bytesToSend + chunkBytes <= 4096) {\n chunksToSend++;\n bytesToSend += chunkBytes;\n } else {\n break;\n }\n }\n\n // Create merged buffer from selected chunks\n const chunksForBatch = this.sendBuffer.slice(0, chunksToSend);\n const totalSamples = chunksForBatch.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of chunksForBatch) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send batched PCM data to main thread\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: chunksToSend,\n totalBytes: merged.byteLength\n });\n \n // Remove sent chunks from buffer (sliding window)\n this.sendBuffer = this.sendBuffer.slice(chunksToSend);\n this.sendBufferBytes -= bytesToSend;\n }\n }\n \n // Flush any remaining buffered data\n flushBuffer() {\n if (this.sendBuffer && this.sendBuffer.length > 0) {\n // Merge remaining chunks\n const totalSamples = this.sendBuffer.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of this.sendBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send remaining data\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: this.sendBuffer.length,\n totalBytes: merged.byteLength,\n isFlush: true\n });\n \n // Reset buffer\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n }\n}\n\n// Register the processor\nregisterProcessor('audio-processor', AudioProcessor);\n";
|
|
9173
|
+
module.exports = "/**\n * AudioProcessor - AudioWorklet for real-time audio processing\n * \n * This AudioWorklet processes audio data in real-time and sends it to the main thread\n * for transmission to the WebSocket server.\n */\n\nclass AudioProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n \n // Configuration\n this.config = options.processorOptions || {};\n // Use AudioContext sampleRate (available as global 'sampleRate' in AudioWorkletProcessor)\n // Fall back to config if sampleRate not available, default to 24 kHz to match typical server/TTS output\n this.sampleRate = typeof sampleRate !== 'undefined' ? sampleRate : (this.config.sampleRate || this.config.outputSampleRate || 24000);\n this.bufferSize = 128; // Process 128 samples at a time (256 bytes = 8ms at 16kHz)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n \n // VAD (Voice Activity Detection) parameters\n this.silenceThreshold = 0.02; // Base RMS threshold for voice detection (scaled by micSensitivity below)\n this.VOICE_FRAMES_REQUIRED = 2; // Require 2 consecutive frames above threshold before activating\n this.minVoiceDuration = 100; // ms - minimum speech duration\n this.pauseThreshold = 3000; // ms - longer pause before processing\n\n // Mic sensitivity: user/agent-adjustable multiplier on the VAD energy gate.\n // 1.0 = default; 2.0 = twice as sensitive (half the RMS floor); 0.5 = half as\n // sensitive (double the floor — only close/loud speech triggers, e.g. TV in the room).\n this.micSensitivity = (typeof this.config.micSensitivity === 'number' && this.config.micSensitivity > 0)\n ? this.config.micSensitivity : 1.0;\n // While agent audio plays, the gate rises by this factor (stricter barge-in: background\n // TV/chatter must not interrupt playback). The SDK main thread tracks playback state.\n this.playbackActive = false;\n this.PLAYBACK_GATE_FACTOR = 1.6;\n // Hysteresis: once voice is active the floor drops, so a borderline signal isn't\n // chopped on every frame (quiet speech tails stay under the trigger level).\n this.HYSTERESIS_FACTOR = 0.75;\n \n // VAD state\n this.isVoiceActive = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.silenceFramesThreshold = 5; // More frames needed for silence detection\n this.voiceFrameCount = 0; // Count consecutive frames above threshold\n \n // Audio quality tracking\n this.frameCount = 0;\n this.lastLogTime = 0;\n \n // Continuous recording mode\n this.continuousMode = true; // Always send audio when voice is detected\n this.forceContinuous = true; // Force continuous for toggle button behavior\n this.isCurrentlyStreaming = false; // Track if we're currently sending audio\n \n // Batching buffer\n this.sendBuffer = null;\n this.sendBufferBytes = 0;\n\n // Start true so the first process() call cannot terminate the worklet before\n // the main thread posts setForceContinuous (WebKit/iOS often renders audio before port messages are handled).\n this.isProcessing = true;\n \n // Handle messages from main thread\n this.port.onmessage = (event) => {\n const { type, data } = event.data;\n \n switch (type) {\n case 'start':\n this.isProcessing = true;\n this.isCurrentlyStreaming = true;\n break;\n \n case 'stop':\n this.isProcessing = false;\n this.isCurrentlyStreaming = false;\n this.isVoiceActive = false;\n this.forceContinuous = false;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Flush any remaining data\n this.flushBuffer();\n break;\n \n case 'setForceContinuous':\n this.forceContinuous = data.enabled;\n this.isProcessing = true;\n // iOS: bypass client VAD - always send. Desktop: keep VAD (saves bandwidth, reduces noise).\n this.isCurrentlyStreaming = data.enabled && (data.bypassVad === true);\n break;\n\n case 'setMicSensitivity': {\n const v = Number(data && data.value);\n if (isFinite(v) && v > 0) {\n this.micSensitivity = Math.max(0.25, Math.min(4.0, v));\n }\n break;\n }\n\n case 'setPlaybackActive':\n this.playbackActive = !!(data && data.active);\n break;\n \n case 'flush':\n this.flushBuffer();\n break;\n \n case 'config':\n Object.assign(this.config, data);\n break;\n }\n };\n }\n \n /**\n * Process audio data\n */\n process(inputs, outputs, parameters) {\n // CRITICAL: If processing is stopped, terminate the processor\n if (!this.isProcessing) {\n // Return false to terminate the AudioWorklet processor\n // This stops all VAD processing immediately\n return false;\n }\n \n const input = inputs[0];\n const output = outputs[0];\n \n // Copy input to output (pass-through)\n if (input.length > 0 && output.length > 0) {\n output[0].set(input[0]);\n }\n \n // Process audio for PCM recording and VAD\n if (input.length > 0 && input[0].length > 0) {\n this.processAudioData(input[0]);\n }\n \n // Keep the processor alive\n return true;\n }\n \n processAudioData(audioData) {\n // CRITICAL: Early return if processing is stopped\n // This prevents VAD calculations and logging when stopped\n if (!this.isProcessing) {\n return;\n }\n \n this.frameCount++;\n \n // Process audio in consistent 128-sample chunks (256 bytes)\n for (let i = 0; i < audioData.length; i += this.bufferSize) {\n const chunkSize = Math.min(this.bufferSize, audioData.length - i);\n \n // Copy chunk to buffer\n for (let j = 0; j < chunkSize; j++) {\n this.buffer[j] = audioData[i + j];\n }\n \n // Pad with zeros if needed\n for (let j = chunkSize; j < this.bufferSize; j++) {\n this.buffer[j] = 0;\n }\n \n // Calculate RMS for VAD on this chunk\n let sum = 0;\n for (let j = 0; j < this.bufferSize; j++) {\n sum += this.buffer[j] * this.buffer[j];\n }\n const rms = Math.sqrt(sum / this.bufferSize);\n \n // Calculate additional features for better VAD\n let variation = 0;\n let highFreqCount = 0;\n for (let j = 1; j < this.bufferSize; j++) {\n const diff = Math.abs(this.buffer[j] - this.buffer[j-1]);\n variation += diff;\n if (diff > 0.1) highFreqCount++;\n }\n variation = variation / this.bufferSize;\n const highFreqRatio = highFreqCount / this.bufferSize;\n \n const currentTime = Date.now();\n\n // Effective VAD floor: base threshold scaled by the mic-sensitivity multiplier —\n // QUADRATIC below 1.0 so the strict end actually bites (0.5 → 0.08 floor, not\n // 0.04: a linear ±6dB band is imperceptible, and browser AGC flattens level\n // differences before we see them). Raised during agent playback (stricter\n // barge-in), lowered while voice is already active (hysteresis, so speech\n // tails aren't clipped).\n const sens = this.micSensitivity;\n let effectiveThreshold = sens >= 1\n ? this.silenceThreshold / sens\n : this.silenceThreshold / (sens * sens);\n if (this.playbackActive) effectiveThreshold *= this.PLAYBACK_GATE_FACTOR;\n if (this.isVoiceActive) effectiveThreshold *= this.HYSTERESIS_FACTOR;\n\n // VAD with reduced sensitivity - require consecutive frames above threshold\n let hasVoice = rms > effectiveThreshold;\n // Calculate time since last voice detection\n const timeSinceLastVoice = currentTime - this.lastVoiceTime;\n\n // Voice detection logic - require consecutive frames above threshold\n if (hasVoice) {\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount++; // Increment consecutive voice frame count\n\n // Only activate streaming after required consecutive frames\n if (this.voiceFrameCount >= this.VOICE_FRAMES_REQUIRED) {\n // Start voice if needed\n if (!this.isVoiceActive) {\n this.isVoiceActive = true;\n this.voiceStartTime = currentTime;\n this.isCurrentlyStreaming = true;\n // Log voice detection (every 50 frames = ~400ms to avoid spam)\n if (this.frameCount % 50 === 0) {\n console.log(`🎤 VAD: VOICE DETECTED (RMS: ${rms.toFixed(4)}, frames: ${this.voiceFrameCount})`);\n }\n }\n }\n\n this.lastVoiceTime = currentTime;\n } else {\n // Silence detected - reset voice frame count\n this.voiceFrameCount = 0;\n this.consecutiveSilenceFrames++;\n \n // In continuous mode, we still use VAD but require longer silence before stopping\n // In non-continuous mode, stop quickly\n const silenceThreshold = this.forceContinuous ? 3000 : 200; // 3s for continuous, 200ms otherwise\n \n // FIXED: Stop condition - also check isCurrentlyStreaming (not just isVoiceActive)\n // This handles case where setForceContinuous set streaming=true but no voice was detected yet\n if (!hasVoice && (this.isVoiceActive || this.isCurrentlyStreaming) && timeSinceLastVoice >= silenceThreshold) {\n this.isVoiceActive = false;\n this.isCurrentlyStreaming = false;\n this.voiceStartTime = 0;\n this.lastVoiceTime = 0;\n this.consecutiveSilenceFrames = 0;\n this.voiceFrameCount = 0; // Reset voice frame count\n // Log silence detection\n console.log(`🔇 VAD: SILENCE DETECTED (${timeSinceLastVoice}ms silence, RMS: ${rms.toFixed(4)})`);\n }\n }\n\n // Send PCM **only if streaming and processing** - hard gate\n // This ensures we only send audio when voice is detected, even in continuous mode\n if (this.isCurrentlyStreaming && this.isProcessing) {\n // Log occasionally when sending (every 200 frames = ~1.6 seconds to avoid spam)\n if (this.frameCount % 200 === 0) {\n console.log(`📤 VAD: Sending audio (isVoiceActive: ${this.isVoiceActive}, RMS: ${rms.toFixed(4)})`);\n }\n this.sendPCMAudioData(this.buffer);\n } else {\n // Log occasionally when blocking (every 200 frames)\n if (this.frameCount % 200 === 0 && this.isProcessing) {\n console.log(`🚫 VAD: Blocking audio (isCurrentlyStreaming: ${this.isCurrentlyStreaming}, RMS: ${rms.toFixed(4)})`);\n }\n }\n }\n }\n \n sendPCMAudioData(float32Data) {\n // Convert Float32Array (-1.0 to 1.0) to Int16Array (-32768 to 32767)\n const pcmData = new Int16Array(float32Data.length);\n \n for (let i = 0; i < float32Data.length; i++) {\n // Clamp and convert to 16-bit PCM\n const sample = Math.max(-1.0, Math.min(1.0, float32Data[i]));\n pcmData[i] = Math.round(sample * 32767);\n }\n \n // Initialize send buffer if not exists\n if (!this.sendBuffer) {\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n \n // Accumulate chunks in buffer\n this.sendBuffer.push(pcmData);\n this.sendBufferBytes += pcmData.byteLength;\n \n // Send in ~4 KB batches (≈128 ms of audio at 16kHz)\n // Use sliding window approach to maintain continuous flow\n while (this.sendBufferBytes >= 4096) {\n // Calculate how many chunks we need for ~4KB\n let chunksToSend = 0;\n let bytesToSend = 0;\n \n for (let i = 0; i < this.sendBuffer.length; i++) {\n const chunkBytes = this.sendBuffer[i].byteLength;\n if (bytesToSend + chunkBytes <= 4096) {\n chunksToSend++;\n bytesToSend += chunkBytes;\n } else {\n break;\n }\n }\n\n // Create merged buffer from selected chunks\n const chunksForBatch = this.sendBuffer.slice(0, chunksToSend);\n const totalSamples = chunksForBatch.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of chunksForBatch) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send batched PCM data to main thread\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: chunksToSend,\n totalBytes: merged.byteLength\n });\n \n // Remove sent chunks from buffer (sliding window)\n this.sendBuffer = this.sendBuffer.slice(chunksToSend);\n this.sendBufferBytes -= bytesToSend;\n }\n }\n \n // Flush any remaining buffered data\n flushBuffer() {\n if (this.sendBuffer && this.sendBuffer.length > 0) {\n // Merge remaining chunks\n const totalSamples = this.sendBuffer.reduce((a, b) => a + b.length, 0);\n const merged = new Int16Array(totalSamples);\n let offset = 0;\n \n for (const chunk of this.sendBuffer) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n \n // Send remaining data\n this.port.postMessage({\n type: 'pcm_audio_data',\n data: merged, // Send the Int16Array directly, not the buffer\n sampleRate: this.sampleRate,\n channelCount: 1,\n frameCount: this.frameCount,\n batchSize: this.sendBuffer.length,\n totalBytes: merged.byteLength,\n isFlush: true\n });\n \n // Reset buffer\n this.sendBuffer = [];\n this.sendBufferBytes = 0;\n }\n }\n}\n\n// Register the processor\nregisterProcessor('audio-processor', AudioProcessor);\n";
|
|
9174
9174
|
|
|
9175
9175
|
/***/ }),
|
|
9176
9176
|
|
|
@@ -10073,6 +10073,15 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10073
10073
|
case 8:
|
|
10074
10074
|
this.mediaStream = _context4.v;
|
|
10075
10075
|
case 9:
|
|
10076
|
+
// Fresh track: it always starts with the getUserMedia defaults (AGC on), so a
|
|
10077
|
+
// persisted low sensitivity must re-apply its AGC-off constraint on it.
|
|
10078
|
+
if (typeof this.config.micSensitivity === 'number' && this.config.micSensitivity < 0.999) {
|
|
10079
|
+
this._agcApplied = undefined;
|
|
10080
|
+
this._applyAgcForSensitivity(this.config.micSensitivity);
|
|
10081
|
+
}
|
|
10082
|
+
|
|
10083
|
+
// CRITICAL: Check connection status AFTER getting permission but BEFORE creating AudioWorkletNode
|
|
10084
|
+
// Server might have rejected during the async permission request
|
|
10076
10085
|
if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
|
|
10077
10086
|
_context4.n = 10;
|
|
10078
10087
|
break;
|
|
@@ -10211,7 +10220,8 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10211
10220
|
this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor', {
|
|
10212
10221
|
processorOptions: {
|
|
10213
10222
|
sampleRate: sampleRate,
|
|
10214
|
-
outputSampleRate: this.config.outputSampleRate
|
|
10223
|
+
outputSampleRate: this.config.outputSampleRate,
|
|
10224
|
+
micSensitivity: this.config.micSensitivity || 1.0
|
|
10215
10225
|
}
|
|
10216
10226
|
});
|
|
10217
10227
|
|
|
@@ -10333,9 +10343,81 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10333
10343
|
return start;
|
|
10334
10344
|
}()
|
|
10335
10345
|
/**
|
|
10336
|
-
*
|
|
10346
|
+
* Mic sensitivity (0.5–2.0, 1.0 = default). Scales the worklet's VAD energy floor:
|
|
10347
|
+
* lower values make the mic LESS trigger-happy (background TV/chatter stops opening
|
|
10348
|
+
* the gate); higher values pick up quieter speech. Applies live when recording and
|
|
10349
|
+
* is carried into processorOptions on the next start().
|
|
10337
10350
|
*/
|
|
10338
10351
|
)
|
|
10352
|
+
}, {
|
|
10353
|
+
key: "setMicSensitivity",
|
|
10354
|
+
value: function setMicSensitivity(value) {
|
|
10355
|
+
var v = Number(value);
|
|
10356
|
+
var clamped = isFinite(v) && v > 0 ? Math.max(0.25, Math.min(4.0, v)) : 1.0;
|
|
10357
|
+
this.config.micSensitivity = clamped;
|
|
10358
|
+
if (this.audioWorkletNode) {
|
|
10359
|
+
this.audioWorkletNode.port.postMessage({
|
|
10360
|
+
type: 'setMicSensitivity',
|
|
10361
|
+
data: {
|
|
10362
|
+
value: clamped
|
|
10363
|
+
}
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
this._applyAgcForSensitivity(clamped);
|
|
10367
|
+
}
|
|
10368
|
+
|
|
10369
|
+
/**
|
|
10370
|
+
* AGC fights the sensitivity gate: it re-amplifies quiet background noise (a TV
|
|
10371
|
+
* across the room) toward speech level before the worklet's VAD ever measures it,
|
|
10372
|
+
* so a lower floor alone changes nothing. Below sensitivity 1.0 we disable auto
|
|
10373
|
+
* gain control on the live track (Chrome supports applyConstraints without a
|
|
10374
|
+
* stream restart); at/above 1.0 the platform default (AGC on) is restored.
|
|
10375
|
+
*/
|
|
10376
|
+
}, {
|
|
10377
|
+
key: "_applyAgcForSensitivity",
|
|
10378
|
+
value: function _applyAgcForSensitivity(sensitivity) {
|
|
10379
|
+
var _this$mediaStream,
|
|
10380
|
+
_this$mediaStream$get,
|
|
10381
|
+
_track$getCapabilitie,
|
|
10382
|
+
_this3 = this;
|
|
10383
|
+
var wantAgc = !(typeof sensitivity === 'number' && sensitivity < 0.999);
|
|
10384
|
+
if (this._agcApplied === wantAgc) return;
|
|
10385
|
+
var track = (_this$mediaStream = this.mediaStream) === null || _this$mediaStream === void 0 || (_this$mediaStream$get = _this$mediaStream.getAudioTracks) === null || _this$mediaStream$get === void 0 ? void 0 : _this$mediaStream$get.call(_this$mediaStream)[0];
|
|
10386
|
+
if (!track || track.readyState !== 'live' || typeof track.applyConstraints !== 'function') return;
|
|
10387
|
+
var caps = (_track$getCapabilitie = track.getCapabilities) === null || _track$getCapabilitie === void 0 ? void 0 : _track$getCapabilitie.call(track);
|
|
10388
|
+
if (caps !== null && caps !== void 0 && caps.autoGainControl && !caps.autoGainControl.includes(wantAgc)) return;
|
|
10389
|
+
// Mark before the async call so rapid slider moves don't stack constraint calls.
|
|
10390
|
+
this._agcApplied = wantAgc;
|
|
10391
|
+
track.applyConstraints({
|
|
10392
|
+
autoGainControl: wantAgc
|
|
10393
|
+
}).then(function () {
|
|
10394
|
+
return console.log("\uD83C\uDF99\uFE0F AudioRecorder: autoGainControl ".concat(wantAgc ? 'ON (default)' : 'OFF (low mic sensitivity)'));
|
|
10395
|
+
}).catch(function (e) {
|
|
10396
|
+
_this3._agcApplied = undefined;
|
|
10397
|
+
console.warn('⚠️ AudioRecorder: applyConstraints(autoGainControl) failed:', e);
|
|
10398
|
+
});
|
|
10399
|
+
}
|
|
10400
|
+
|
|
10401
|
+
/**
|
|
10402
|
+
* Tell the worklet whether agent audio is currently playing. While playing, the VAD
|
|
10403
|
+
* floor rises (stricter barge-in) so room noise doesn't interrupt the agent.
|
|
10404
|
+
*/
|
|
10405
|
+
}, {
|
|
10406
|
+
key: "setPlaybackActive",
|
|
10407
|
+
value: function setPlaybackActive(active) {
|
|
10408
|
+
if (this.audioWorkletNode) {
|
|
10409
|
+
this.audioWorkletNode.port.postMessage({
|
|
10410
|
+
type: 'setPlaybackActive',
|
|
10411
|
+
data: {
|
|
10412
|
+
active: active === true
|
|
10413
|
+
}
|
|
10414
|
+
});
|
|
10415
|
+
}
|
|
10416
|
+
}
|
|
10417
|
+
|
|
10418
|
+
/**
|
|
10419
|
+
* Stop audio recording
|
|
10420
|
+
*/
|
|
10339
10421
|
}, {
|
|
10340
10422
|
key: "stop",
|
|
10341
10423
|
value: (function () {
|
|
@@ -12047,7 +12129,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
12047
12129
|
|
|
12048
12130
|
// SDK build time for debugging
|
|
12049
12131
|
if (true) {
|
|
12050
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
12132
|
+
helloMessage.lastBuildTime = "2026-08-21T10:43:17.433Z";
|
|
12051
12133
|
}
|
|
12052
12134
|
try {
|
|
12053
12135
|
this.ws.send(JSON.stringify(helloMessage));
|
|
@@ -21357,8 +21439,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
|
21357
21439
|
|
|
21358
21440
|
|
|
21359
21441
|
// Version - injected at build time from package.json via webpack DefinePlugin
|
|
21360
|
-
var VERSION = "2.48.
|
|
21361
|
-
var BUILD_TIME = "2026-08-
|
|
21442
|
+
var VERSION = "2.48.16";
|
|
21443
|
+
var BUILD_TIME = "2026-08-21T10:43:17.433Z";
|
|
21362
21444
|
console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
|
|
21363
21445
|
|
|
21364
21446
|
// Named exports
|
|
@@ -28480,6 +28562,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28480
28562
|
_this._playbackGateFlushing = false;
|
|
28481
28563
|
_this.isRecording = false;
|
|
28482
28564
|
_this.isPlaying = false;
|
|
28565
|
+
|
|
28566
|
+
/**
|
|
28567
|
+
* Mic sensitivity (VAD energy-gate multiplier, 0.5–2.0, 1.0 = default).
|
|
28568
|
+
* _agentMicSensitivity comes from hello_ack (agent configuration default);
|
|
28569
|
+
* _micSensitivity is the effective value actually applied to the recorder —
|
|
28570
|
+
* a user override from the widget settings popover wins over the agent default.
|
|
28571
|
+
*/
|
|
28572
|
+
_this._agentMicSensitivity = 1.0;
|
|
28573
|
+
_this._micSensitivity = 1.0;
|
|
28483
28574
|
_this.isDestroyed = false;
|
|
28484
28575
|
_this.isPaused = false;
|
|
28485
28576
|
_this.outputAudioFormat = null;
|
|
@@ -28902,6 +28993,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28902
28993
|
var _this3 = this;
|
|
28903
28994
|
this.audioPlayer.on('playbackStarted', function (info) {
|
|
28904
28995
|
_this3.isPlaying = true;
|
|
28996
|
+
|
|
28997
|
+
// Raise the mic VAD floor while the agent speaks (stricter barge-in gate —
|
|
28998
|
+
// room noise / TV must not interrupt playback)
|
|
28999
|
+
if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
|
|
29000
|
+
_this3.audioRecorder.setPlaybackActive(true);
|
|
29001
|
+
}
|
|
28905
29002
|
_this3.emit('playbackStarted');
|
|
28906
29003
|
|
|
28907
29004
|
// Transcripts are displayed when they arrive (between sentences)
|
|
@@ -28946,6 +29043,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28946
29043
|
});
|
|
28947
29044
|
this.audioPlayer.on('playbackStopped', function (info) {
|
|
28948
29045
|
_this3.isPlaying = false;
|
|
29046
|
+
|
|
29047
|
+
// Back to the idle mic VAD floor
|
|
29048
|
+
if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
|
|
29049
|
+
_this3.audioRecorder.setPlaybackActive(false);
|
|
29050
|
+
}
|
|
28949
29051
|
_this3.emit('playbackStopped');
|
|
28950
29052
|
|
|
28951
29053
|
// When playback stops, keep currentPlayingSentenceId to track which sentence just finished
|
|
@@ -29315,7 +29417,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29315
29417
|
// iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
|
|
29316
29418
|
var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
|
|
29317
29419
|
var env = {
|
|
29318
|
-
sdkVersion: true ? "2.48.
|
|
29420
|
+
sdkVersion: true ? "2.48.16" : 0,
|
|
29319
29421
|
ua: ua,
|
|
29320
29422
|
platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
|
|
29321
29423
|
mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
|
|
@@ -29330,7 +29432,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29330
29432
|
} catch (e) {
|
|
29331
29433
|
console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
|
|
29332
29434
|
return {
|
|
29333
|
-
sdkVersion: true ? "2.48.
|
|
29435
|
+
sdkVersion: true ? "2.48.16" : 0
|
|
29334
29436
|
};
|
|
29335
29437
|
}
|
|
29336
29438
|
}
|
|
@@ -29472,7 +29574,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29472
29574
|
|
|
29473
29575
|
// Include SDK build time for debugging
|
|
29474
29576
|
if (true) {
|
|
29475
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
29577
|
+
helloMessage.lastBuildTime = "2026-08-21T10:43:17.433Z";
|
|
29476
29578
|
}
|
|
29477
29579
|
|
|
29478
29580
|
// Client environment (device/browser/webview) for backend logs + Langfuse metadata
|
|
@@ -30136,6 +30238,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
30136
30238
|
this.emit('inputFormatNegotiated', message.inputAudioFormat);
|
|
30137
30239
|
}
|
|
30138
30240
|
|
|
30241
|
+
// Agent-configured mic sensitivity default (0.5–2.0). Applies only when the user
|
|
30242
|
+
// has no explicit/persisted override — _syncCallSettingsAfterHello() below
|
|
30243
|
+
// reapplies the override on top when one exists.
|
|
30244
|
+
if (typeof message.micSensitivity === 'number' && isFinite(message.micSensitivity) && message.micSensitivity > 0) {
|
|
30245
|
+
this._agentMicSensitivity = Math.max(0.5, Math.min(2.0, message.micSensitivity));
|
|
30246
|
+
if (!this._callSettings || typeof this._callSettings.micSensitivity !== 'number') {
|
|
30247
|
+
this._applyMicSensitivity(this._agentMicSensitivity);
|
|
30248
|
+
}
|
|
30249
|
+
}
|
|
30250
|
+
|
|
30139
30251
|
// Server-driven disclaimer gate (voice): when true, client must send disclaimer_ack before using the session.
|
|
30140
30252
|
this.disclaimersPending = message.disclaimersRequired === true;
|
|
30141
30253
|
this.disclaimersHash = this.disclaimersPending ? message.disclaimersHash || null : null;
|
|
@@ -31006,7 +31118,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31006
31118
|
* Called before hello_ack, the values are kept and flushed silently by
|
|
31007
31119
|
* _syncCallSettingsAfterHello() once the session is ready.
|
|
31008
31120
|
*
|
|
31009
|
-
*
|
|
31121
|
+
* `micSensitivity` (0.5–2.0, 1.0 = default) scales the mic VAD energy gate and is
|
|
31122
|
+
* enforced LOCALLY in the recorder (it also rides the wire so the backend can log it).
|
|
31123
|
+
*
|
|
31124
|
+
* @param {{voiceSpeedFactor?: number, responseLength?: string, micSensitivity?: number}} settings
|
|
31010
31125
|
* @param {{announce?: boolean}} [opts]
|
|
31011
31126
|
* @returns {boolean} true if the message was sent now
|
|
31012
31127
|
*/
|
|
@@ -31016,6 +31131,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31016
31131
|
var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
31017
31132
|
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
31018
31133
|
this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
|
|
31134
|
+
if (typeof settings.micSensitivity === 'number') {
|
|
31135
|
+
this._applyMicSensitivity(settings.micSensitivity);
|
|
31136
|
+
}
|
|
31019
31137
|
if (!this.helloAckReceived || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
|
|
31020
31138
|
return false; // flushed after hello_ack
|
|
31021
31139
|
}
|
|
@@ -31029,9 +31147,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31029
31147
|
if (this._callSettings.responseLength) {
|
|
31030
31148
|
msg.responseLength = this._callSettings.responseLength;
|
|
31031
31149
|
}
|
|
31150
|
+
if (typeof this._callSettings.micSensitivity === 'number') {
|
|
31151
|
+
msg.micSensitivity = this._callSettings.micSensitivity;
|
|
31152
|
+
}
|
|
31032
31153
|
return this.sendMessage(msg);
|
|
31033
31154
|
}
|
|
31034
31155
|
|
|
31156
|
+
/**
|
|
31157
|
+
* Apply mic sensitivity to the capture path immediately (clamped 0.5–2.0).
|
|
31158
|
+
* The recorder carries it in its config, so it also survives a mid-call
|
|
31159
|
+
* recording restart.
|
|
31160
|
+
*/
|
|
31161
|
+
}, {
|
|
31162
|
+
key: "_applyMicSensitivity",
|
|
31163
|
+
value: function _applyMicSensitivity(value) {
|
|
31164
|
+
var v = Number(value);
|
|
31165
|
+
this._micSensitivity = isFinite(v) && v > 0 ? Math.max(0.5, Math.min(2.0, v)) : 1.0;
|
|
31166
|
+
if (this.audioRecorder && typeof this.audioRecorder.setMicSensitivity === 'function') {
|
|
31167
|
+
this.audioRecorder.setMicSensitivity(this._micSensitivity);
|
|
31168
|
+
}
|
|
31169
|
+
}
|
|
31170
|
+
|
|
31035
31171
|
/**
|
|
31036
31172
|
* Flush persisted call settings right after hello_ack (silent — no spoken ack).
|
|
31037
31173
|
* Values come from an earlier setCallSettings() call or, when none, from
|
|
@@ -31050,16 +31186,25 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31050
31186
|
this._callSettings = _objectSpread({}, s);
|
|
31051
31187
|
var factor = typeof s.voiceSpeedFactor === 'number' ? s.voiceSpeedFactor : 1.0;
|
|
31052
31188
|
var length = s.responseLength || 'normal';
|
|
31053
|
-
|
|
31054
|
-
|
|
31189
|
+
var sens = typeof s.micSensitivity === 'number' ? s.micSensitivity : 1.0;
|
|
31190
|
+
if (typeof s.micSensitivity === 'number') {
|
|
31191
|
+
this._applyMicSensitivity(sens);
|
|
31192
|
+
}
|
|
31193
|
+
if (Math.abs(factor - 1.0) < 0.001 && length === 'normal' && Math.abs(sens - 1.0) < 0.001) return;
|
|
31194
|
+
var msg = {
|
|
31055
31195
|
t: 'call_settings',
|
|
31056
31196
|
voiceSpeedFactor: factor,
|
|
31057
31197
|
responseLength: length,
|
|
31058
31198
|
announce: false
|
|
31059
|
-
}
|
|
31199
|
+
};
|
|
31200
|
+
if (typeof s.micSensitivity === 'number') {
|
|
31201
|
+
msg.micSensitivity = sens;
|
|
31202
|
+
}
|
|
31203
|
+
this.sendMessage(msg);
|
|
31060
31204
|
console.log('⚙️ VoiceSDK v2: Synced persisted call settings:', {
|
|
31061
31205
|
voiceSpeedFactor: factor,
|
|
31062
|
-
responseLength: length
|
|
31206
|
+
responseLength: length,
|
|
31207
|
+
micSensitivity: sens
|
|
31063
31208
|
});
|
|
31064
31209
|
} catch (e) {
|
|
31065
31210
|
console.warn('⚙️ VoiceSDK v2: Failed to sync call settings (ignored):', e);
|
|
@@ -35658,7 +35803,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35658
35803
|
return;
|
|
35659
35804
|
}
|
|
35660
35805
|
this._ensureAboutStyles();
|
|
35661
|
-
var version = true ? "2.48.
|
|
35806
|
+
var version = true ? "2.48.16" : 0;
|
|
35662
35807
|
var convId = this._getLastConversationId();
|
|
35663
35808
|
var t = function t(k, fb) {
|
|
35664
35809
|
try {
|
|
@@ -35729,16 +35874,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35729
35874
|
}
|
|
35730
35875
|
|
|
35731
35876
|
/**
|
|
35732
|
-
* Load persisted call settings. Defaults: agent-configured speed (factor 1.0)
|
|
35733
|
-
*
|
|
35734
|
-
* @returns {{voiceSpeedFactor: number, responseLength: string}}
|
|
35877
|
+
* Load persisted call settings. Defaults: agent-configured speed (factor 1.0),
|
|
35878
|
+
* "normal" response length, and default mic sensitivity (1.0).
|
|
35879
|
+
* @returns {{voiceSpeedFactor: number, responseLength: string, micSensitivity: number}}
|
|
35735
35880
|
*/
|
|
35736
35881
|
}, {
|
|
35737
35882
|
key: "_loadCallSettings",
|
|
35738
35883
|
value: function _loadCallSettings() {
|
|
35739
35884
|
var defaults = {
|
|
35740
35885
|
voiceSpeedFactor: 1.0,
|
|
35741
|
-
responseLength: 'normal'
|
|
35886
|
+
responseLength: 'normal',
|
|
35887
|
+
micSensitivity: 1.0
|
|
35742
35888
|
};
|
|
35743
35889
|
try {
|
|
35744
35890
|
var raw = localStorage.getItem(this._callSettingsStorageKey());
|
|
@@ -35746,9 +35892,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35746
35892
|
var s = JSON.parse(raw);
|
|
35747
35893
|
var factor = typeof s.voiceSpeedFactor === 'number' ? Math.max(0.5, Math.min(2.0, s.voiceSpeedFactor)) : 1.0;
|
|
35748
35894
|
var length = ['detailed', 'normal', 'brief', 'very_brief'].includes(s.responseLength) ? s.responseLength : 'normal';
|
|
35895
|
+
var sens = typeof s.micSensitivity === 'number' ? Math.max(0.5, Math.min(2.0, s.micSensitivity)) : 1.0;
|
|
35749
35896
|
return {
|
|
35750
35897
|
voiceSpeedFactor: factor,
|
|
35751
|
-
responseLength: length
|
|
35898
|
+
responseLength: length,
|
|
35899
|
+
micSensitivity: sens
|
|
35752
35900
|
};
|
|
35753
35901
|
} catch (_) {
|
|
35754
35902
|
return defaults;
|
|
@@ -35779,7 +35927,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35779
35927
|
if (voice !== null && voice !== void 0 && voice.setCallSettings) {
|
|
35780
35928
|
voice.setCallSettings({
|
|
35781
35929
|
voiceSpeedFactor: merged.voiceSpeedFactor,
|
|
35782
|
-
responseLength: merged.responseLength
|
|
35930
|
+
responseLength: merged.responseLength,
|
|
35931
|
+
micSensitivity: merged.micSensitivity
|
|
35783
35932
|
}, {
|
|
35784
35933
|
announce: announce
|
|
35785
35934
|
});
|
|
@@ -35829,6 +35978,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35829
35978
|
var title = t('settingsTitle', 'Call settings');
|
|
35830
35979
|
var speedLabel = t('settingsVoiceSpeed', 'Voice speed');
|
|
35831
35980
|
var lengthLabel = t('settingsResponseLength', 'Response length');
|
|
35981
|
+
var sensLabel = t('settingsMicSensitivity', 'Mic sensitivity');
|
|
35982
|
+
var sensLow = t('sensitivityLow', 'Low');
|
|
35983
|
+
var sensHigh = t('sensitivityHigh', 'High');
|
|
35832
35984
|
// Slider order: very brief (left) → detailed (right); higher = more detail.
|
|
35833
35985
|
var LEVELS = ['very_brief', 'brief', 'normal', 'detailed'];
|
|
35834
35986
|
var levelLabels = {
|
|
@@ -35841,23 +35993,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35841
35993
|
var levelIdx = Math.max(0, LEVELS.indexOf(saved.responseLength));
|
|
35842
35994
|
var overlay = document.createElement('div');
|
|
35843
35995
|
overlay.className = 'ttp-about-overlay';
|
|
35844
|
-
overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(speedLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-speed-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-speed\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.voiceSpeedFactor, "\"\n aria-label=\"").concat(speedLabel, "\">\n <div class=\"ttp-cs-ends\"><span>0.5\xD7</span><span>2\xD7</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(lengthLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-length-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-length\n min=\"0\" max=\"3\" step=\"1\" value=\"").concat(levelIdx, "\"\n aria-label=\"").concat(lengthLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(levelLabels.very_brief, "</span><span>").concat(levelLabels.detailed, "</span></div>\n </div>\n </div>\n </div>");
|
|
35996
|
+
overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(speedLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-speed-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-speed\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.voiceSpeedFactor, "\"\n aria-label=\"").concat(speedLabel, "\">\n <div class=\"ttp-cs-ends\"><span>0.5\xD7</span><span>2\xD7</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(lengthLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-length-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-length\n min=\"0\" max=\"3\" step=\"1\" value=\"").concat(levelIdx, "\"\n aria-label=\"").concat(lengthLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(levelLabels.very_brief, "</span><span>").concat(levelLabels.detailed, "</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(sensLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-sens-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-sens\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.micSensitivity, "\"\n aria-label=\"").concat(sensLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(sensLow, "</span><span>").concat(sensHigh, "</span></div>\n </div>\n </div>\n </div>");
|
|
35845
35997
|
var speedSlider = overlay.querySelector('[data-cs-speed]');
|
|
35846
35998
|
var speedVal = overlay.querySelector('[data-cs-speed-val]');
|
|
35847
35999
|
var lengthSlider = overlay.querySelector('[data-cs-length]');
|
|
35848
36000
|
var lengthVal = overlay.querySelector('[data-cs-length-val]');
|
|
36001
|
+
var sensSlider = overlay.querySelector('[data-cs-sens]');
|
|
36002
|
+
var sensVal = overlay.querySelector('[data-cs-sens-val]');
|
|
35849
36003
|
var fmtSpeed = function fmtSpeed(v) {
|
|
35850
36004
|
return "".concat(Number(v).toFixed(2).replace(/0$/, ''), "\xD7");
|
|
35851
36005
|
};
|
|
35852
36006
|
var renderVals = function renderVals() {
|
|
35853
36007
|
if (speedVal) speedVal.textContent = fmtSpeed(speedSlider.value);
|
|
35854
36008
|
if (lengthVal) lengthVal.textContent = levelLabels[LEVELS[Number(lengthSlider.value)]] || '';
|
|
36009
|
+
if (sensVal) sensVal.textContent = fmtSpeed(sensSlider.value);
|
|
35855
36010
|
};
|
|
35856
36011
|
renderVals();
|
|
35857
36012
|
|
|
35858
36013
|
// Live label updates while dragging; persist + push on release only.
|
|
35859
36014
|
speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('input', renderVals);
|
|
35860
36015
|
lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('input', renderVals);
|
|
36016
|
+
sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('input', renderVals);
|
|
35861
36017
|
speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('change', function () {
|
|
35862
36018
|
_this9._applyCallSettings({
|
|
35863
36019
|
voiceSpeedFactor: Number(speedSlider.value)
|
|
@@ -35868,6 +36024,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35868
36024
|
responseLength: LEVELS[Number(lengthSlider.value)]
|
|
35869
36025
|
}, true);
|
|
35870
36026
|
});
|
|
36027
|
+
// Mic sensitivity is enforced locally in the capture path — no spoken ack needed.
|
|
36028
|
+
sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('change', function () {
|
|
36029
|
+
_this9._applyCallSettings({
|
|
36030
|
+
micSensitivity: Number(sensSlider.value)
|
|
36031
|
+
}, false);
|
|
36032
|
+
});
|
|
35871
36033
|
var close = function close() {
|
|
35872
36034
|
return _this9._closeCallSettingsModal();
|
|
35873
36035
|
};
|
|
@@ -45283,7 +45445,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45283
45445
|
"lengthDetailed": "Detailed",
|
|
45284
45446
|
"lengthNormal": "Normal",
|
|
45285
45447
|
"lengthBrief": "Brief",
|
|
45286
|
-
"lengthVeryBrief": "Very brief"
|
|
45448
|
+
"lengthVeryBrief": "Very brief",
|
|
45449
|
+
"settingsMicSensitivity": "Mic sensitivity",
|
|
45450
|
+
"sensitivityLow": "Low",
|
|
45451
|
+
"sensitivityHigh": "High"
|
|
45287
45452
|
},
|
|
45288
45453
|
"he": {
|
|
45289
45454
|
"landingTitle": "איך תרצה לתקשר?",
|
|
@@ -45329,7 +45494,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45329
45494
|
"lengthDetailed": "מפורט",
|
|
45330
45495
|
"lengthNormal": "רגיל",
|
|
45331
45496
|
"lengthBrief": "קצר",
|
|
45332
|
-
"lengthVeryBrief": "קצר מאוד"
|
|
45497
|
+
"lengthVeryBrief": "קצר מאוד",
|
|
45498
|
+
"settingsMicSensitivity": "רגישות מיקרופון",
|
|
45499
|
+
"sensitivityLow": "נמוכה",
|
|
45500
|
+
"sensitivityHigh": "גבוהה"
|
|
45333
45501
|
},
|
|
45334
45502
|
"ar": {
|
|
45335
45503
|
"landingTitle": "كيف تريد التواصل؟",
|
|
@@ -45375,7 +45543,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45375
45543
|
"lengthDetailed": "مفصّل",
|
|
45376
45544
|
"lengthNormal": "عادي",
|
|
45377
45545
|
"lengthBrief": "مختصر",
|
|
45378
|
-
"lengthVeryBrief": "مختصر جداً"
|
|
45546
|
+
"lengthVeryBrief": "مختصر جداً",
|
|
45547
|
+
"settingsMicSensitivity": "حساسية الميكروفون",
|
|
45548
|
+
"sensitivityLow": "منخفضة",
|
|
45549
|
+
"sensitivityHigh": "مرتفعة"
|
|
45379
45550
|
},
|
|
45380
45551
|
"ru": {
|
|
45381
45552
|
"landingTitle": "Как вы хотите общаться?",
|
|
@@ -45421,7 +45592,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45421
45592
|
"lengthDetailed": "Подробно",
|
|
45422
45593
|
"lengthNormal": "Обычно",
|
|
45423
45594
|
"lengthBrief": "Кратко",
|
|
45424
|
-
"lengthVeryBrief": "Очень кратко"
|
|
45595
|
+
"lengthVeryBrief": "Очень кратко",
|
|
45596
|
+
"settingsMicSensitivity": "Чувствительность микрофона",
|
|
45597
|
+
"sensitivityLow": "Низкая",
|
|
45598
|
+
"sensitivityHigh": "Высокая"
|
|
45425
45599
|
},
|
|
45426
45600
|
"es": {
|
|
45427
45601
|
"landingTitle": "¿Cómo te gustaría comunicarte?",
|
|
@@ -45467,7 +45641,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45467
45641
|
"lengthDetailed": "Detallada",
|
|
45468
45642
|
"lengthNormal": "Normal",
|
|
45469
45643
|
"lengthBrief": "Breve",
|
|
45470
|
-
"lengthVeryBrief": "Muy breve"
|
|
45644
|
+
"lengthVeryBrief": "Muy breve",
|
|
45645
|
+
"settingsMicSensitivity": "Sensibilidad del micrófono",
|
|
45646
|
+
"sensitivityLow": "Baja",
|
|
45647
|
+
"sensitivityHigh": "Alta"
|
|
45471
45648
|
},
|
|
45472
45649
|
"fr": {
|
|
45473
45650
|
"landingTitle": "Comment souhaitez-vous communiquer?",
|
|
@@ -45513,7 +45690,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45513
45690
|
"lengthDetailed": "Détaillée",
|
|
45514
45691
|
"lengthNormal": "Normale",
|
|
45515
45692
|
"lengthBrief": "Brève",
|
|
45516
|
-
"lengthVeryBrief": "Très brève"
|
|
45693
|
+
"lengthVeryBrief": "Très brève",
|
|
45694
|
+
"settingsMicSensitivity": "Sensibilité du micro",
|
|
45695
|
+
"sensitivityLow": "Faible",
|
|
45696
|
+
"sensitivityHigh": "Élevée"
|
|
45517
45697
|
},
|
|
45518
45698
|
"de": {
|
|
45519
45699
|
"landingTitle": "Wie möchten Sie kommunizieren?",
|
|
@@ -45559,7 +45739,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45559
45739
|
"lengthDetailed": "Ausführlich",
|
|
45560
45740
|
"lengthNormal": "Normal",
|
|
45561
45741
|
"lengthBrief": "Kurz",
|
|
45562
|
-
"lengthVeryBrief": "Sehr kurz"
|
|
45742
|
+
"lengthVeryBrief": "Sehr kurz",
|
|
45743
|
+
"settingsMicSensitivity": "Mikrofonempfindlichkeit",
|
|
45744
|
+
"sensitivityLow": "Niedrig",
|
|
45745
|
+
"sensitivityHigh": "Hoch"
|
|
45563
45746
|
}
|
|
45564
45747
|
});
|
|
45565
45748
|
|