ttp-agent-sdk 2.48.11 → 2.48.14
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 +199 -32
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.js +1 -1
- package/dist/audio-processor.js +35 -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 // raised during agent playback (stricter barge-in), lowered while voice is\n // already active (hysteresis, so speech tails aren't clipped).\n let effectiveThreshold = this.silenceThreshold / this.micSensitivity;\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
|
|
|
@@ -10211,7 +10211,8 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10211
10211
|
this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor', {
|
|
10212
10212
|
processorOptions: {
|
|
10213
10213
|
sampleRate: sampleRate,
|
|
10214
|
-
outputSampleRate: this.config.outputSampleRate
|
|
10214
|
+
outputSampleRate: this.config.outputSampleRate,
|
|
10215
|
+
micSensitivity: this.config.micSensitivity || 1.0
|
|
10215
10216
|
}
|
|
10216
10217
|
});
|
|
10217
10218
|
|
|
@@ -10333,9 +10334,48 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10333
10334
|
return start;
|
|
10334
10335
|
}()
|
|
10335
10336
|
/**
|
|
10336
|
-
*
|
|
10337
|
+
* Mic sensitivity (0.5–2.0, 1.0 = default). Scales the worklet's VAD energy floor:
|
|
10338
|
+
* lower values make the mic LESS trigger-happy (background TV/chatter stops opening
|
|
10339
|
+
* the gate); higher values pick up quieter speech. Applies live when recording and
|
|
10340
|
+
* is carried into processorOptions on the next start().
|
|
10337
10341
|
*/
|
|
10338
10342
|
)
|
|
10343
|
+
}, {
|
|
10344
|
+
key: "setMicSensitivity",
|
|
10345
|
+
value: function setMicSensitivity(value) {
|
|
10346
|
+
var v = Number(value);
|
|
10347
|
+
var clamped = isFinite(v) && v > 0 ? Math.max(0.25, Math.min(4.0, v)) : 1.0;
|
|
10348
|
+
this.config.micSensitivity = clamped;
|
|
10349
|
+
if (this.audioWorkletNode) {
|
|
10350
|
+
this.audioWorkletNode.port.postMessage({
|
|
10351
|
+
type: 'setMicSensitivity',
|
|
10352
|
+
data: {
|
|
10353
|
+
value: clamped
|
|
10354
|
+
}
|
|
10355
|
+
});
|
|
10356
|
+
}
|
|
10357
|
+
}
|
|
10358
|
+
|
|
10359
|
+
/**
|
|
10360
|
+
* Tell the worklet whether agent audio is currently playing. While playing, the VAD
|
|
10361
|
+
* floor rises (stricter barge-in) so room noise doesn't interrupt the agent.
|
|
10362
|
+
*/
|
|
10363
|
+
}, {
|
|
10364
|
+
key: "setPlaybackActive",
|
|
10365
|
+
value: function setPlaybackActive(active) {
|
|
10366
|
+
if (this.audioWorkletNode) {
|
|
10367
|
+
this.audioWorkletNode.port.postMessage({
|
|
10368
|
+
type: 'setPlaybackActive',
|
|
10369
|
+
data: {
|
|
10370
|
+
active: active === true
|
|
10371
|
+
}
|
|
10372
|
+
});
|
|
10373
|
+
}
|
|
10374
|
+
}
|
|
10375
|
+
|
|
10376
|
+
/**
|
|
10377
|
+
* Stop audio recording
|
|
10378
|
+
*/
|
|
10339
10379
|
}, {
|
|
10340
10380
|
key: "stop",
|
|
10341
10381
|
value: (function () {
|
|
@@ -12047,7 +12087,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
12047
12087
|
|
|
12048
12088
|
// SDK build time for debugging
|
|
12049
12089
|
if (true) {
|
|
12050
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
12090
|
+
helloMessage.lastBuildTime = "2026-08-21T10:23:43.383Z";
|
|
12051
12091
|
}
|
|
12052
12092
|
try {
|
|
12053
12093
|
this.ws.send(JSON.stringify(helloMessage));
|
|
@@ -21357,8 +21397,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
|
21357
21397
|
|
|
21358
21398
|
|
|
21359
21399
|
// Version - injected at build time from package.json via webpack DefinePlugin
|
|
21360
|
-
var VERSION = "2.48.
|
|
21361
|
-
var BUILD_TIME = "2026-08-
|
|
21400
|
+
var VERSION = "2.48.14";
|
|
21401
|
+
var BUILD_TIME = "2026-08-21T10:23:43.383Z";
|
|
21362
21402
|
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
21403
|
|
|
21364
21404
|
// Named exports
|
|
@@ -27091,7 +27131,31 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27091
27131
|
return flag === '1';
|
|
27092
27132
|
}
|
|
27093
27133
|
} catch (e) {/* non-browser env */}
|
|
27094
|
-
|
|
27134
|
+
if (typeof this.config.htmlAudioPlayback === 'boolean') {
|
|
27135
|
+
return this.config.htmlAudioPlayback;
|
|
27136
|
+
}
|
|
27137
|
+
// Default: the hop (AudioContext → MediaStreamDestination → HTMLAudioElement)
|
|
27138
|
+
// gives Chrome's AEC a far-end reference, but Chrome plays that live stream
|
|
27139
|
+
// with adaptive jitter-buffer speed control, which audibly wobbles on
|
|
27140
|
+
// desktop Linux (PipeWire/PulseAudio). Off there, on elsewhere.
|
|
27141
|
+
var hopOn = !this._isDesktopLinux();
|
|
27142
|
+
if (!this._hopDefaultLogged) {
|
|
27143
|
+
this._hopDefaultLogged = true;
|
|
27144
|
+
console.log("\uD83D\uDD0A AudioPlayer: htmlAudioPlayback default \u2192 ".concat(hopOn ? 'on (AEC far-end route)' : 'off (desktop Linux: hop causes playback speed wobble)'));
|
|
27145
|
+
}
|
|
27146
|
+
return hopOn;
|
|
27147
|
+
}
|
|
27148
|
+
}, {
|
|
27149
|
+
key: "_isDesktopLinux",
|
|
27150
|
+
value: function _isDesktopLinux() {
|
|
27151
|
+
try {
|
|
27152
|
+
var ua = navigator.userAgent || '';
|
|
27153
|
+
if (/Android|iPhone|iPad|iPod/i.test(ua)) return false;
|
|
27154
|
+
var platform = navigator.userAgentData && navigator.userAgentData.platform || navigator.platform || '';
|
|
27155
|
+
return /linux/i.test(platform) || /Linux|X11/i.test(ua);
|
|
27156
|
+
} catch (e) {
|
|
27157
|
+
return false;
|
|
27158
|
+
}
|
|
27095
27159
|
}
|
|
27096
27160
|
}, {
|
|
27097
27161
|
key: "_ttsSampleRate",
|
|
@@ -28414,7 +28478,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28414
28478
|
} catch (e) {
|
|
28415
28479
|
console.log('🔧 VoiceSDK v2: ttp_hop check threw (storage blocked?):', e && e.message);
|
|
28416
28480
|
}
|
|
28417
|
-
|
|
28481
|
+
// Pass through undefined so AudioPlayer applies the platform default
|
|
28482
|
+
return config.htmlAudioPlayback;
|
|
28418
28483
|
}(),
|
|
28419
28484
|
// Protocol version
|
|
28420
28485
|
|
|
@@ -28455,6 +28520,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28455
28520
|
_this._playbackGateFlushing = false;
|
|
28456
28521
|
_this.isRecording = false;
|
|
28457
28522
|
_this.isPlaying = false;
|
|
28523
|
+
|
|
28524
|
+
/**
|
|
28525
|
+
* Mic sensitivity (VAD energy-gate multiplier, 0.5–2.0, 1.0 = default).
|
|
28526
|
+
* _agentMicSensitivity comes from hello_ack (agent configuration default);
|
|
28527
|
+
* _micSensitivity is the effective value actually applied to the recorder —
|
|
28528
|
+
* a user override from the widget settings popover wins over the agent default.
|
|
28529
|
+
*/
|
|
28530
|
+
_this._agentMicSensitivity = 1.0;
|
|
28531
|
+
_this._micSensitivity = 1.0;
|
|
28458
28532
|
_this.isDestroyed = false;
|
|
28459
28533
|
_this.isPaused = false;
|
|
28460
28534
|
_this.outputAudioFormat = null;
|
|
@@ -28877,6 +28951,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28877
28951
|
var _this3 = this;
|
|
28878
28952
|
this.audioPlayer.on('playbackStarted', function (info) {
|
|
28879
28953
|
_this3.isPlaying = true;
|
|
28954
|
+
|
|
28955
|
+
// Raise the mic VAD floor while the agent speaks (stricter barge-in gate —
|
|
28956
|
+
// room noise / TV must not interrupt playback)
|
|
28957
|
+
if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
|
|
28958
|
+
_this3.audioRecorder.setPlaybackActive(true);
|
|
28959
|
+
}
|
|
28880
28960
|
_this3.emit('playbackStarted');
|
|
28881
28961
|
|
|
28882
28962
|
// Transcripts are displayed when they arrive (between sentences)
|
|
@@ -28921,6 +29001,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
28921
29001
|
});
|
|
28922
29002
|
this.audioPlayer.on('playbackStopped', function (info) {
|
|
28923
29003
|
_this3.isPlaying = false;
|
|
29004
|
+
|
|
29005
|
+
// Back to the idle mic VAD floor
|
|
29006
|
+
if (_this3.audioRecorder && typeof _this3.audioRecorder.setPlaybackActive === 'function') {
|
|
29007
|
+
_this3.audioRecorder.setPlaybackActive(false);
|
|
29008
|
+
}
|
|
28924
29009
|
_this3.emit('playbackStopped');
|
|
28925
29010
|
|
|
28926
29011
|
// When playback stops, keep currentPlayingSentenceId to track which sentence just finished
|
|
@@ -29290,7 +29375,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29290
29375
|
// iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
|
|
29291
29376
|
var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
|
|
29292
29377
|
var env = {
|
|
29293
|
-
sdkVersion: true ? "2.48.
|
|
29378
|
+
sdkVersion: true ? "2.48.14" : 0,
|
|
29294
29379
|
ua: ua,
|
|
29295
29380
|
platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
|
|
29296
29381
|
mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
|
|
@@ -29305,7 +29390,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29305
29390
|
} catch (e) {
|
|
29306
29391
|
console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
|
|
29307
29392
|
return {
|
|
29308
|
-
sdkVersion: true ? "2.48.
|
|
29393
|
+
sdkVersion: true ? "2.48.14" : 0
|
|
29309
29394
|
};
|
|
29310
29395
|
}
|
|
29311
29396
|
}
|
|
@@ -29447,7 +29532,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
29447
29532
|
|
|
29448
29533
|
// Include SDK build time for debugging
|
|
29449
29534
|
if (true) {
|
|
29450
|
-
helloMessage.lastBuildTime = "2026-08-
|
|
29535
|
+
helloMessage.lastBuildTime = "2026-08-21T10:23:43.383Z";
|
|
29451
29536
|
}
|
|
29452
29537
|
|
|
29453
29538
|
// Client environment (device/browser/webview) for backend logs + Langfuse metadata
|
|
@@ -30111,6 +30196,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
30111
30196
|
this.emit('inputFormatNegotiated', message.inputAudioFormat);
|
|
30112
30197
|
}
|
|
30113
30198
|
|
|
30199
|
+
// Agent-configured mic sensitivity default (0.5–2.0). Applies only when the user
|
|
30200
|
+
// has no explicit/persisted override — _syncCallSettingsAfterHello() below
|
|
30201
|
+
// reapplies the override on top when one exists.
|
|
30202
|
+
if (typeof message.micSensitivity === 'number' && isFinite(message.micSensitivity) && message.micSensitivity > 0) {
|
|
30203
|
+
this._agentMicSensitivity = Math.max(0.5, Math.min(2.0, message.micSensitivity));
|
|
30204
|
+
if (!this._callSettings || typeof this._callSettings.micSensitivity !== 'number') {
|
|
30205
|
+
this._applyMicSensitivity(this._agentMicSensitivity);
|
|
30206
|
+
}
|
|
30207
|
+
}
|
|
30208
|
+
|
|
30114
30209
|
// Server-driven disclaimer gate (voice): when true, client must send disclaimer_ack before using the session.
|
|
30115
30210
|
this.disclaimersPending = message.disclaimersRequired === true;
|
|
30116
30211
|
this.disclaimersHash = this.disclaimersPending ? message.disclaimersHash || null : null;
|
|
@@ -30981,7 +31076,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
30981
31076
|
* Called before hello_ack, the values are kept and flushed silently by
|
|
30982
31077
|
* _syncCallSettingsAfterHello() once the session is ready.
|
|
30983
31078
|
*
|
|
30984
|
-
*
|
|
31079
|
+
* `micSensitivity` (0.5–2.0, 1.0 = default) scales the mic VAD energy gate and is
|
|
31080
|
+
* enforced LOCALLY in the recorder (it also rides the wire so the backend can log it).
|
|
31081
|
+
*
|
|
31082
|
+
* @param {{voiceSpeedFactor?: number, responseLength?: string, micSensitivity?: number}} settings
|
|
30985
31083
|
* @param {{announce?: boolean}} [opts]
|
|
30986
31084
|
* @returns {boolean} true if the message was sent now
|
|
30987
31085
|
*/
|
|
@@ -30991,6 +31089,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
30991
31089
|
var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
30992
31090
|
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
30993
31091
|
this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
|
|
31092
|
+
if (typeof settings.micSensitivity === 'number') {
|
|
31093
|
+
this._applyMicSensitivity(settings.micSensitivity);
|
|
31094
|
+
}
|
|
30994
31095
|
if (!this.helloAckReceived || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
|
|
30995
31096
|
return false; // flushed after hello_ack
|
|
30996
31097
|
}
|
|
@@ -31004,9 +31105,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31004
31105
|
if (this._callSettings.responseLength) {
|
|
31005
31106
|
msg.responseLength = this._callSettings.responseLength;
|
|
31006
31107
|
}
|
|
31108
|
+
if (typeof this._callSettings.micSensitivity === 'number') {
|
|
31109
|
+
msg.micSensitivity = this._callSettings.micSensitivity;
|
|
31110
|
+
}
|
|
31007
31111
|
return this.sendMessage(msg);
|
|
31008
31112
|
}
|
|
31009
31113
|
|
|
31114
|
+
/**
|
|
31115
|
+
* Apply mic sensitivity to the capture path immediately (clamped 0.5–2.0).
|
|
31116
|
+
* The recorder carries it in its config, so it also survives a mid-call
|
|
31117
|
+
* recording restart.
|
|
31118
|
+
*/
|
|
31119
|
+
}, {
|
|
31120
|
+
key: "_applyMicSensitivity",
|
|
31121
|
+
value: function _applyMicSensitivity(value) {
|
|
31122
|
+
var v = Number(value);
|
|
31123
|
+
this._micSensitivity = isFinite(v) && v > 0 ? Math.max(0.5, Math.min(2.0, v)) : 1.0;
|
|
31124
|
+
if (this.audioRecorder && typeof this.audioRecorder.setMicSensitivity === 'function') {
|
|
31125
|
+
this.audioRecorder.setMicSensitivity(this._micSensitivity);
|
|
31126
|
+
}
|
|
31127
|
+
}
|
|
31128
|
+
|
|
31010
31129
|
/**
|
|
31011
31130
|
* Flush persisted call settings right after hello_ack (silent — no spoken ack).
|
|
31012
31131
|
* Values come from an earlier setCallSettings() call or, when none, from
|
|
@@ -31025,16 +31144,25 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
31025
31144
|
this._callSettings = _objectSpread({}, s);
|
|
31026
31145
|
var factor = typeof s.voiceSpeedFactor === 'number' ? s.voiceSpeedFactor : 1.0;
|
|
31027
31146
|
var length = s.responseLength || 'normal';
|
|
31028
|
-
|
|
31029
|
-
|
|
31147
|
+
var sens = typeof s.micSensitivity === 'number' ? s.micSensitivity : 1.0;
|
|
31148
|
+
if (typeof s.micSensitivity === 'number') {
|
|
31149
|
+
this._applyMicSensitivity(sens);
|
|
31150
|
+
}
|
|
31151
|
+
if (Math.abs(factor - 1.0) < 0.001 && length === 'normal' && Math.abs(sens - 1.0) < 0.001) return;
|
|
31152
|
+
var msg = {
|
|
31030
31153
|
t: 'call_settings',
|
|
31031
31154
|
voiceSpeedFactor: factor,
|
|
31032
31155
|
responseLength: length,
|
|
31033
31156
|
announce: false
|
|
31034
|
-
}
|
|
31157
|
+
};
|
|
31158
|
+
if (typeof s.micSensitivity === 'number') {
|
|
31159
|
+
msg.micSensitivity = sens;
|
|
31160
|
+
}
|
|
31161
|
+
this.sendMessage(msg);
|
|
31035
31162
|
console.log('⚙️ VoiceSDK v2: Synced persisted call settings:', {
|
|
31036
31163
|
voiceSpeedFactor: factor,
|
|
31037
|
-
responseLength: length
|
|
31164
|
+
responseLength: length,
|
|
31165
|
+
micSensitivity: sens
|
|
31038
31166
|
});
|
|
31039
31167
|
} catch (e) {
|
|
31040
31168
|
console.warn('⚙️ VoiceSDK v2: Failed to sync call settings (ignored):', e);
|
|
@@ -33125,7 +33253,8 @@ var AgentSDK = /*#__PURE__*/function () {
|
|
|
33125
33253
|
outputBitDepth: this.config.outputBitDepth || 16,
|
|
33126
33254
|
// Default: 16-bit
|
|
33127
33255
|
flavor: this.config.flavor || null,
|
|
33128
|
-
htmlAudioPlayback: this.config.htmlAudioPlayback
|
|
33256
|
+
htmlAudioPlayback: this.config.htmlAudioPlayback,
|
|
33257
|
+
// undefined → AudioPlayer applies platform default
|
|
33129
33258
|
// Shared client-tool handler map (set by TTPChatWidget so voice + text SDKs
|
|
33130
33259
|
// both look up handlers in the same registration site).
|
|
33131
33260
|
sharedToolHandlers: this.config.sharedToolHandlers || null,
|
|
@@ -35632,7 +35761,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35632
35761
|
return;
|
|
35633
35762
|
}
|
|
35634
35763
|
this._ensureAboutStyles();
|
|
35635
|
-
var version = true ? "2.48.
|
|
35764
|
+
var version = true ? "2.48.14" : 0;
|
|
35636
35765
|
var convId = this._getLastConversationId();
|
|
35637
35766
|
var t = function t(k, fb) {
|
|
35638
35767
|
try {
|
|
@@ -35703,16 +35832,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35703
35832
|
}
|
|
35704
35833
|
|
|
35705
35834
|
/**
|
|
35706
|
-
* Load persisted call settings. Defaults: agent-configured speed (factor 1.0)
|
|
35707
|
-
*
|
|
35708
|
-
* @returns {{voiceSpeedFactor: number, responseLength: string}}
|
|
35835
|
+
* Load persisted call settings. Defaults: agent-configured speed (factor 1.0),
|
|
35836
|
+
* "normal" response length, and default mic sensitivity (1.0).
|
|
35837
|
+
* @returns {{voiceSpeedFactor: number, responseLength: string, micSensitivity: number}}
|
|
35709
35838
|
*/
|
|
35710
35839
|
}, {
|
|
35711
35840
|
key: "_loadCallSettings",
|
|
35712
35841
|
value: function _loadCallSettings() {
|
|
35713
35842
|
var defaults = {
|
|
35714
35843
|
voiceSpeedFactor: 1.0,
|
|
35715
|
-
responseLength: 'normal'
|
|
35844
|
+
responseLength: 'normal',
|
|
35845
|
+
micSensitivity: 1.0
|
|
35716
35846
|
};
|
|
35717
35847
|
try {
|
|
35718
35848
|
var raw = localStorage.getItem(this._callSettingsStorageKey());
|
|
@@ -35720,9 +35850,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35720
35850
|
var s = JSON.parse(raw);
|
|
35721
35851
|
var factor = typeof s.voiceSpeedFactor === 'number' ? Math.max(0.5, Math.min(2.0, s.voiceSpeedFactor)) : 1.0;
|
|
35722
35852
|
var length = ['detailed', 'normal', 'brief', 'very_brief'].includes(s.responseLength) ? s.responseLength : 'normal';
|
|
35853
|
+
var sens = typeof s.micSensitivity === 'number' ? Math.max(0.5, Math.min(2.0, s.micSensitivity)) : 1.0;
|
|
35723
35854
|
return {
|
|
35724
35855
|
voiceSpeedFactor: factor,
|
|
35725
|
-
responseLength: length
|
|
35856
|
+
responseLength: length,
|
|
35857
|
+
micSensitivity: sens
|
|
35726
35858
|
};
|
|
35727
35859
|
} catch (_) {
|
|
35728
35860
|
return defaults;
|
|
@@ -35753,7 +35885,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35753
35885
|
if (voice !== null && voice !== void 0 && voice.setCallSettings) {
|
|
35754
35886
|
voice.setCallSettings({
|
|
35755
35887
|
voiceSpeedFactor: merged.voiceSpeedFactor,
|
|
35756
|
-
responseLength: merged.responseLength
|
|
35888
|
+
responseLength: merged.responseLength,
|
|
35889
|
+
micSensitivity: merged.micSensitivity
|
|
35757
35890
|
}, {
|
|
35758
35891
|
announce: announce
|
|
35759
35892
|
});
|
|
@@ -35803,6 +35936,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35803
35936
|
var title = t('settingsTitle', 'Call settings');
|
|
35804
35937
|
var speedLabel = t('settingsVoiceSpeed', 'Voice speed');
|
|
35805
35938
|
var lengthLabel = t('settingsResponseLength', 'Response length');
|
|
35939
|
+
var sensLabel = t('settingsMicSensitivity', 'Mic sensitivity');
|
|
35940
|
+
var sensLow = t('sensitivityLow', 'Low');
|
|
35941
|
+
var sensHigh = t('sensitivityHigh', 'High');
|
|
35806
35942
|
// Slider order: very brief (left) → detailed (right); higher = more detail.
|
|
35807
35943
|
var LEVELS = ['very_brief', 'brief', 'normal', 'detailed'];
|
|
35808
35944
|
var levelLabels = {
|
|
@@ -35815,23 +35951,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35815
35951
|
var levelIdx = Math.max(0, LEVELS.indexOf(saved.responseLength));
|
|
35816
35952
|
var overlay = document.createElement('div');
|
|
35817
35953
|
overlay.className = 'ttp-about-overlay';
|
|
35818
|
-
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>");
|
|
35954
|
+
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>");
|
|
35819
35955
|
var speedSlider = overlay.querySelector('[data-cs-speed]');
|
|
35820
35956
|
var speedVal = overlay.querySelector('[data-cs-speed-val]');
|
|
35821
35957
|
var lengthSlider = overlay.querySelector('[data-cs-length]');
|
|
35822
35958
|
var lengthVal = overlay.querySelector('[data-cs-length-val]');
|
|
35959
|
+
var sensSlider = overlay.querySelector('[data-cs-sens]');
|
|
35960
|
+
var sensVal = overlay.querySelector('[data-cs-sens-val]');
|
|
35823
35961
|
var fmtSpeed = function fmtSpeed(v) {
|
|
35824
35962
|
return "".concat(Number(v).toFixed(2).replace(/0$/, ''), "\xD7");
|
|
35825
35963
|
};
|
|
35826
35964
|
var renderVals = function renderVals() {
|
|
35827
35965
|
if (speedVal) speedVal.textContent = fmtSpeed(speedSlider.value);
|
|
35828
35966
|
if (lengthVal) lengthVal.textContent = levelLabels[LEVELS[Number(lengthSlider.value)]] || '';
|
|
35967
|
+
if (sensVal) sensVal.textContent = fmtSpeed(sensSlider.value);
|
|
35829
35968
|
};
|
|
35830
35969
|
renderVals();
|
|
35831
35970
|
|
|
35832
35971
|
// Live label updates while dragging; persist + push on release only.
|
|
35833
35972
|
speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('input', renderVals);
|
|
35834
35973
|
lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('input', renderVals);
|
|
35974
|
+
sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('input', renderVals);
|
|
35835
35975
|
speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('change', function () {
|
|
35836
35976
|
_this9._applyCallSettings({
|
|
35837
35977
|
voiceSpeedFactor: Number(speedSlider.value)
|
|
@@ -35842,6 +35982,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
35842
35982
|
responseLength: LEVELS[Number(lengthSlider.value)]
|
|
35843
35983
|
}, true);
|
|
35844
35984
|
});
|
|
35985
|
+
// Mic sensitivity is enforced locally in the capture path — no spoken ack needed.
|
|
35986
|
+
sensSlider === null || sensSlider === void 0 || sensSlider.addEventListener('change', function () {
|
|
35987
|
+
_this9._applyCallSettings({
|
|
35988
|
+
micSensitivity: Number(sensSlider.value)
|
|
35989
|
+
}, false);
|
|
35990
|
+
});
|
|
35845
35991
|
var close = function close() {
|
|
35846
35992
|
return _this9._closeCallSettingsModal();
|
|
35847
35993
|
};
|
|
@@ -45257,7 +45403,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45257
45403
|
"lengthDetailed": "Detailed",
|
|
45258
45404
|
"lengthNormal": "Normal",
|
|
45259
45405
|
"lengthBrief": "Brief",
|
|
45260
|
-
"lengthVeryBrief": "Very brief"
|
|
45406
|
+
"lengthVeryBrief": "Very brief",
|
|
45407
|
+
"settingsMicSensitivity": "Mic sensitivity",
|
|
45408
|
+
"sensitivityLow": "Low",
|
|
45409
|
+
"sensitivityHigh": "High"
|
|
45261
45410
|
},
|
|
45262
45411
|
"he": {
|
|
45263
45412
|
"landingTitle": "איך תרצה לתקשר?",
|
|
@@ -45303,7 +45452,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45303
45452
|
"lengthDetailed": "מפורט",
|
|
45304
45453
|
"lengthNormal": "רגיל",
|
|
45305
45454
|
"lengthBrief": "קצר",
|
|
45306
|
-
"lengthVeryBrief": "קצר מאוד"
|
|
45455
|
+
"lengthVeryBrief": "קצר מאוד",
|
|
45456
|
+
"settingsMicSensitivity": "רגישות מיקרופון",
|
|
45457
|
+
"sensitivityLow": "נמוכה",
|
|
45458
|
+
"sensitivityHigh": "גבוהה"
|
|
45307
45459
|
},
|
|
45308
45460
|
"ar": {
|
|
45309
45461
|
"landingTitle": "كيف تريد التواصل؟",
|
|
@@ -45349,7 +45501,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45349
45501
|
"lengthDetailed": "مفصّل",
|
|
45350
45502
|
"lengthNormal": "عادي",
|
|
45351
45503
|
"lengthBrief": "مختصر",
|
|
45352
|
-
"lengthVeryBrief": "مختصر جداً"
|
|
45504
|
+
"lengthVeryBrief": "مختصر جداً",
|
|
45505
|
+
"settingsMicSensitivity": "حساسية الميكروفون",
|
|
45506
|
+
"sensitivityLow": "منخفضة",
|
|
45507
|
+
"sensitivityHigh": "مرتفعة"
|
|
45353
45508
|
},
|
|
45354
45509
|
"ru": {
|
|
45355
45510
|
"landingTitle": "Как вы хотите общаться?",
|
|
@@ -45395,7 +45550,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45395
45550
|
"lengthDetailed": "Подробно",
|
|
45396
45551
|
"lengthNormal": "Обычно",
|
|
45397
45552
|
"lengthBrief": "Кратко",
|
|
45398
|
-
"lengthVeryBrief": "Очень кратко"
|
|
45553
|
+
"lengthVeryBrief": "Очень кратко",
|
|
45554
|
+
"settingsMicSensitivity": "Чувствительность микрофона",
|
|
45555
|
+
"sensitivityLow": "Низкая",
|
|
45556
|
+
"sensitivityHigh": "Высокая"
|
|
45399
45557
|
},
|
|
45400
45558
|
"es": {
|
|
45401
45559
|
"landingTitle": "¿Cómo te gustaría comunicarte?",
|
|
@@ -45441,7 +45599,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45441
45599
|
"lengthDetailed": "Detallada",
|
|
45442
45600
|
"lengthNormal": "Normal",
|
|
45443
45601
|
"lengthBrief": "Breve",
|
|
45444
|
-
"lengthVeryBrief": "Muy breve"
|
|
45602
|
+
"lengthVeryBrief": "Muy breve",
|
|
45603
|
+
"settingsMicSensitivity": "Sensibilidad del micrófono",
|
|
45604
|
+
"sensitivityLow": "Baja",
|
|
45605
|
+
"sensitivityHigh": "Alta"
|
|
45445
45606
|
},
|
|
45446
45607
|
"fr": {
|
|
45447
45608
|
"landingTitle": "Comment souhaitez-vous communiquer?",
|
|
@@ -45487,7 +45648,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45487
45648
|
"lengthDetailed": "Détaillée",
|
|
45488
45649
|
"lengthNormal": "Normale",
|
|
45489
45650
|
"lengthBrief": "Brève",
|
|
45490
|
-
"lengthVeryBrief": "Très brève"
|
|
45651
|
+
"lengthVeryBrief": "Très brève",
|
|
45652
|
+
"settingsMicSensitivity": "Sensibilité du micro",
|
|
45653
|
+
"sensitivityLow": "Faible",
|
|
45654
|
+
"sensitivityHigh": "Élevée"
|
|
45491
45655
|
},
|
|
45492
45656
|
"de": {
|
|
45493
45657
|
"landingTitle": "Wie möchten Sie kommunizieren?",
|
|
@@ -45533,7 +45697,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
45533
45697
|
"lengthDetailed": "Ausführlich",
|
|
45534
45698
|
"lengthNormal": "Normal",
|
|
45535
45699
|
"lengthBrief": "Kurz",
|
|
45536
|
-
"lengthVeryBrief": "Sehr kurz"
|
|
45700
|
+
"lengthVeryBrief": "Sehr kurz",
|
|
45701
|
+
"settingsMicSensitivity": "Mikrofonempfindlichkeit",
|
|
45702
|
+
"sensitivityLow": "Niedrig",
|
|
45703
|
+
"sensitivityHigh": "Hoch"
|
|
45537
45704
|
}
|
|
45538
45705
|
});
|
|
45539
45706
|
|