@primoia/vocall-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol/types.ts","../src/voice/frame-splitter.ts","../src/voice/web-voice-service.ts","../src/client/vocall-client.ts","../src/context/vocall-provider.tsx","../src/hooks/use-vocall.ts","../src/hooks/use-vocall-field.ts","../src/hooks/use-vocall-action.ts","../src/hooks/use-vocall-voice.ts","../src/components/VocallChat.tsx","../src/components/VocallFab.tsx","../src/components/VocallStatus.tsx"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Vocall AAP Protocol Types (TypeScript port of types.dart / types.go)\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Field & Screen Descriptors (Manifest)\n// ---------------------------------------------------------------------------\n\nexport enum FieldType {\n Text = 'text',\n Number = 'number',\n Currency = 'currency',\n Date = 'date',\n Datetime = 'datetime',\n Email = 'email',\n Phone = 'phone',\n Masked = 'masked',\n Select = 'select',\n Autocomplete = 'autocomplete',\n Checkbox = 'checkbox',\n Radio = 'radio',\n Textarea = 'textarea',\n File = 'file',\n Hidden = 'hidden',\n}\n\nexport interface SelectOption {\n value: string;\n label: string;\n}\n\nexport interface FieldDescriptor {\n id: string;\n type: FieldType | string;\n label: string;\n required?: boolean;\n mask?: string;\n placeholder?: string;\n options?: SelectOption[];\n source?: string;\n min?: number;\n max?: number;\n maxLength?: number;\n readOnly?: boolean;\n}\n\nexport interface ActionDescriptor {\n id: string;\n label: string;\n destructive?: boolean;\n requiresConfirmation?: boolean;\n disabled?: boolean;\n}\n\nexport interface ModalDescriptor {\n id: string;\n label: string;\n searchable?: boolean;\n}\n\nexport interface ScreenDescriptor {\n id: string;\n label: string;\n route?: string;\n fields?: FieldDescriptor[];\n actions?: ActionDescriptor[];\n modals?: ModalDescriptor[];\n}\n\n// ---------------------------------------------------------------------------\n// Persona & User\n// ---------------------------------------------------------------------------\n\nexport interface Persona {\n name?: string;\n role?: string;\n instructions?: string;\n}\n\nexport interface UserInfo {\n name?: string;\n email?: string;\n org?: string;\n role?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Client -> Server messages\n// ---------------------------------------------------------------------------\n\nexport interface ManifestMessage {\n type: 'manifest';\n app: string;\n screens: Record<string, ScreenDescriptor>;\n version?: string;\n currentScreen?: string;\n user?: UserInfo;\n context?: Record<string, unknown>;\n persona?: Persona;\n}\n\nexport interface TextMessage {\n type: 'text';\n message: string;\n}\n\nexport interface ConfirmMessage {\n type: 'confirm';\n seq: number;\n confirmed: boolean;\n}\n\nexport interface ActionResult {\n index: number;\n success: boolean;\n error?: string;\n}\n\nexport interface FieldState {\n value: unknown;\n valid?: boolean;\n error?: string;\n dirty?: boolean;\n}\n\nexport interface StateMessage {\n type: 'state';\n screen: string;\n fields?: Record<string, FieldState>;\n canSubmit?: boolean;\n}\n\nexport interface ResultMessage {\n type: 'result';\n seq: number;\n results: ActionResult[];\n state?: StateMessage;\n}\n\n// ---------------------------------------------------------------------------\n// Server -> Client messages\n// ---------------------------------------------------------------------------\n\nexport interface FeaturesConfig {\n voice?: boolean;\n chat?: boolean;\n}\n\nexport interface ConfigResponse {\n type: 'config';\n sessionId: string;\n features?: FeaturesConfig;\n}\n\nexport interface UIAction {\n do: string;\n screen?: string;\n field?: string;\n action?: string;\n modal?: string;\n value?: unknown;\n query?: string;\n message?: string;\n animate?: string;\n speed?: number;\n duration?: number;\n level?: string;\n}\n\nexport interface CommandMessage {\n type: 'command';\n seq: number;\n actions: UIAction[];\n}\n\nexport interface ChatMessageOut {\n type: 'chat';\n from: string;\n message: string;\n final?: boolean;\n}\n\nexport interface ChatTokenOut {\n type: 'chat_token';\n token: string;\n}\n\nexport interface ChatEndOut {\n type: 'chat_end';\n}\n\nexport interface StatusOut {\n type: 'status';\n status: string;\n}\n\nexport interface ErrorOut {\n type: 'error';\n message: string;\n code?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Chat message model (for the overlay thread)\n// ---------------------------------------------------------------------------\n\nexport enum ChatRole {\n User = 'user',\n Agent = 'agent',\n System = 'system',\n}\n\nexport interface ChatMessage {\n role: ChatRole;\n text: string;\n timestamp: Date;\n}\n\n// ---------------------------------------------------------------------------\n// Status enum\n// ---------------------------------------------------------------------------\n\nexport enum VocallStatus {\n Disconnected = 'disconnected',\n Idle = 'idle',\n Listening = 'listening',\n Recording = 'recording',\n Thinking = 'thinking',\n Speaking = 'speaking',\n Executing = 'executing',\n}\n\n// ---------------------------------------------------------------------------\n// Voice protocol types\n// ---------------------------------------------------------------------------\n\nexport type VoiceState = 'idle' | 'listening' | 'recording' | 'thinking' | 'speaking';\n\n// ---------------------------------------------------------------------------\n// Incoming message union type\n// ---------------------------------------------------------------------------\n\nexport type ServerMessage =\n | ConfigResponse\n | ChatMessageOut\n | ChatTokenOut\n | ChatEndOut\n | StatusOut\n | CommandMessage\n | ErrorOut\n | { type: string; [key: string]: unknown };\n","// ---------------------------------------------------------------------------\n// FrameSplitter - Splits arbitrary-length PCM S16LE byte buffers into\n// fixed-size frames suitable for the Vocall voice streaming protocol.\n// Server counts silence in frames (75 frames = 1.5s at 20ms each).\n// ---------------------------------------------------------------------------\n\nexport class FrameSplitter {\n private readonly frameSizeBytes: number;\n private buffer: Uint8Array;\n private offset = 0;\n\n constructor(frameSizeBytes = 640) {\n this.frameSizeBytes = frameSizeBytes;\n this.buffer = new Uint8Array(frameSizeBytes);\n }\n\n /**\n * Feed arbitrary-length PCM bytes into the splitter.\n * Whenever a full frame is assembled, `emit` is called with a copy.\n */\n feed(pcmBytes: Uint8Array, emit: (frame: Uint8Array) => void): void {\n let srcPos = 0;\n const srcLen = pcmBytes.length;\n while (srcPos < srcLen) {\n const remaining = this.frameSizeBytes - this.offset;\n const available = srcLen - srcPos;\n const toCopy = Math.min(available, remaining);\n this.buffer.set(pcmBytes.subarray(srcPos, srcPos + toCopy), this.offset);\n this.offset += toCopy;\n srcPos += toCopy;\n if (this.offset === this.frameSizeBytes) {\n emit(new Uint8Array(this.buffer));\n this.offset = 0;\n }\n }\n }\n\n /** Reset the internal buffer, discarding any partial frame. */\n reset(): void {\n this.offset = 0;\n }\n\n /** Number of bytes currently buffered (incomplete frame). */\n get buffered(): number {\n return this.offset;\n }\n}\n","// ---------------------------------------------------------------------------\n// WebVoiceService - Web Audio API implementation of VoiceService.\n// Captures PCM S16LE 16kHz mono frames from the microphone and plays\n// back WAV audio chunks received from the server.\n// ---------------------------------------------------------------------------\n\nimport { FrameSplitter } from './frame-splitter';\nimport type { AudioLevelCallback, VoiceService } from './voice-service';\n\n/** Target sample rate for the Vocall voice protocol. */\nconst TARGET_SAMPLE_RATE = 16000;\n\n/** Frame size in bytes (20ms of S16LE @ 16kHz mono = 640 bytes). */\nconst FRAME_SIZE_BYTES = 640;\n\n/** ScriptProcessorNode buffer size. */\nconst SCRIPT_BUFFER_SIZE = 4096;\n\n/** Number of samples to fade-in at the start of each playback chunk. */\nconst FADE_IN_SAMPLES = 128;\n\n/** Silent frame (640 bytes of zeros) sent while mic is muted. */\nconst SILENT_FRAME = new Uint8Array(FRAME_SIZE_BYTES);\n\nexport class WebVoiceService implements VoiceService {\n // -------------------------------------------------------------------------\n // Public state\n // -------------------------------------------------------------------------\n\n onAudioLevel: AudioLevelCallback | null = null;\n onPlaybackComplete: (() => void) | null = null;\n\n private _isCapturing = false;\n private _isPlaying = false;\n private _isMonitoring = false;\n private _isMuted = false;\n\n get isSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices !== 'undefined' &&\n typeof navigator.mediaDevices.getUserMedia === 'function' &&\n typeof AudioContext !== 'undefined'\n );\n }\n\n get isCapturing(): boolean {\n return this._isCapturing;\n }\n\n get isPlaying(): boolean {\n return this._isPlaying;\n }\n\n get isMonitoring(): boolean {\n return this._isMonitoring;\n }\n\n // -------------------------------------------------------------------------\n // Private capture state\n // -------------------------------------------------------------------------\n\n private _audioCtx: AudioContext | null = null;\n private _stream: MediaStream | null = null;\n private _sourceNode: MediaStreamAudioSourceNode | null = null;\n private _scriptNode: ScriptProcessorNode | null = null;\n private _splitter = new FrameSplitter(FRAME_SIZE_BYTES);\n private _sendChunk: ((chunk: Uint8Array) => void) | null = null;\n\n // -------------------------------------------------------------------------\n // Private playback state\n // -------------------------------------------------------------------------\n\n private _playbackCtx: AudioContext | null = null;\n private _playbackQueue: Uint8Array[] = [];\n private _currentSource: AudioBufferSourceNode | null = null;\n private _pendingDecodes = 0;\n\n // -------------------------------------------------------------------------\n // Capture\n // -------------------------------------------------------------------------\n\n async startCapture(sendChunk: (chunk: Uint8Array) => void): Promise<void> {\n if (this._isCapturing) return;\n\n this._sendChunk = sendChunk;\n await this._initCapturePipeline(false);\n this._isCapturing = true;\n }\n\n stopCapture(): void {\n this._teardownCapturePipeline();\n this._isCapturing = false;\n this._sendChunk = null;\n }\n\n // -------------------------------------------------------------------------\n // Monitor mode (capture pipeline without sending)\n // -------------------------------------------------------------------------\n\n async startMonitor(): Promise<void> {\n if (this._isMonitoring) return;\n\n await this._initCapturePipeline(true);\n this._isMonitoring = true;\n }\n\n stopMonitor(): void {\n this._teardownCapturePipeline();\n this._isMonitoring = false;\n }\n\n // -------------------------------------------------------------------------\n // Mute\n // -------------------------------------------------------------------------\n\n muteMic(muted: boolean): void {\n this._isMuted = muted;\n if (this._stream) {\n const tracks = this._stream.getAudioTracks();\n for (const track of tracks) {\n track.enabled = !muted;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Playback\n // -------------------------------------------------------------------------\n\n playAudio(wavData: Uint8Array): void {\n this._playbackQueue.push(wavData);\n this._pendingDecodes++;\n this._isPlaying = true;\n this._decodeAndEnqueue(wavData);\n }\n\n stopPlayback(): void {\n if (this._currentSource) {\n try {\n this._currentSource.onended = null;\n this._currentSource.stop();\n } catch {\n // Already stopped\n }\n this._currentSource = null;\n }\n this._playbackQueue = [];\n this._pendingDecodes = 0;\n this._isPlaying = false;\n }\n\n // -------------------------------------------------------------------------\n // Dispose\n // -------------------------------------------------------------------------\n\n dispose(): void {\n this.stopCapture();\n this.stopMonitor();\n this.stopPlayback();\n\n if (this._audioCtx) {\n this._audioCtx.close().catch(() => {});\n this._audioCtx = null;\n }\n if (this._playbackCtx) {\n this._playbackCtx.close().catch(() => {});\n this._playbackCtx = null;\n }\n\n this.onAudioLevel = null;\n this.onPlaybackComplete = null;\n }\n\n // -------------------------------------------------------------------------\n // Private: capture pipeline\n // -------------------------------------------------------------------------\n\n private async _initCapturePipeline(monitorOnly: boolean): Promise<void> {\n this._stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n channelCount: 1,\n echoCancellation: true,\n noiseSuppression: false,\n autoGainControl: true,\n },\n });\n\n if (!this._audioCtx) {\n this._audioCtx = new AudioContext();\n }\n\n // Resume context if suspended (autoplay policy)\n if (this._audioCtx.state === 'suspended') {\n await this._audioCtx.resume();\n }\n\n const nativeRate = this._audioCtx.sampleRate;\n\n this._sourceNode = this._audioCtx.createMediaStreamSource(this._stream);\n this._scriptNode = this._audioCtx.createScriptProcessor(SCRIPT_BUFFER_SIZE, 1, 1);\n this._splitter.reset();\n\n this._scriptNode.onaudioprocess = (event: AudioProcessingEvent) => {\n const inputData = event.inputBuffer.getChannelData(0);\n\n // Compute RMS audio level\n this._computeAndEmitLevel(inputData);\n\n if (this._isMuted) {\n // Send silent frames to keep the server frame counter alive\n if (!monitorOnly && this._sendChunk) {\n const silentPcm = this._downsampleToS16LE(new Float32Array(inputData.length), nativeRate);\n this._splitter.feed(silentPcm, (frame) => {\n this._sendChunk!(new Uint8Array(SILENT_FRAME));\n });\n }\n return;\n }\n\n // Downsample to S16LE @ 16kHz\n const pcmBytes = this._downsampleToS16LE(inputData, nativeRate);\n\n // Split into fixed frames and send\n this._splitter.feed(pcmBytes, (frame) => {\n if (!monitorOnly && this._sendChunk) {\n this._sendChunk(frame);\n }\n });\n };\n\n this._sourceNode.connect(this._scriptNode);\n this._scriptNode.connect(this._audioCtx.destination);\n }\n\n private _teardownCapturePipeline(): void {\n if (this._scriptNode) {\n this._scriptNode.onaudioprocess = null;\n this._scriptNode.disconnect();\n this._scriptNode = null;\n }\n\n if (this._sourceNode) {\n this._sourceNode.disconnect();\n this._sourceNode = null;\n }\n\n if (this._stream) {\n for (const track of this._stream.getTracks()) {\n track.stop();\n }\n this._stream = null;\n }\n\n this._splitter.reset();\n this._isMuted = false;\n }\n\n // -------------------------------------------------------------------------\n // Private: audio processing\n // -------------------------------------------------------------------------\n\n /**\n * Downsample Float32 audio at native sample rate to S16LE at TARGET_SAMPLE_RATE\n * using linear interpolation.\n */\n private _downsampleToS16LE(input: Float32Array, fromRate: number): Uint8Array {\n const ratio = fromRate / TARGET_SAMPLE_RATE;\n const outputLen = Math.floor(input.length / ratio);\n const output = new Uint8Array(outputLen * 2); // 2 bytes per S16LE sample\n const view = new DataView(output.buffer);\n\n for (let i = 0; i < outputLen; i++) {\n const srcIdx = i * ratio;\n const srcFloor = Math.floor(srcIdx);\n const srcCeil = Math.min(srcFloor + 1, input.length - 1);\n const frac = srcIdx - srcFloor;\n\n // Linear interpolation\n let sample = input[srcFloor] + (input[srcCeil] - input[srcFloor]) * frac;\n\n // Clamp to [-1, 1]\n if (sample > 1.0) sample = 1.0;\n else if (sample < -1.0) sample = -1.0;\n\n // Convert to S16LE\n const s16 = Math.round(sample * 32767);\n view.setInt16(i * 2, s16, true); // little-endian\n }\n\n return output;\n }\n\n /**\n * Compute RMS level from Float32 audio samples.\n * Result is scaled by 4 and clamped to [0, 1].\n */\n private _computeAndEmitLevel(samples: Float32Array): void {\n if (!this.onAudioLevel) return;\n\n let sum = 0;\n for (let i = 0; i < samples.length; i++) {\n sum += samples[i] * samples[i];\n }\n const rms = Math.sqrt(sum / samples.length);\n const level = Math.min(rms * 4, 1.0);\n this.onAudioLevel(level);\n }\n\n // -------------------------------------------------------------------------\n // Private: playback pipeline\n // -------------------------------------------------------------------------\n\n private async _decodeAndEnqueue(wavData: Uint8Array): Promise<void> {\n if (!this._playbackCtx) {\n this._playbackCtx = new AudioContext();\n }\n\n if (this._playbackCtx.state === 'suspended') {\n await this._playbackCtx.resume();\n }\n\n try {\n const arrayBuffer = wavData.buffer.slice(\n wavData.byteOffset,\n wavData.byteOffset + wavData.byteLength,\n ) as ArrayBuffer;\n const audioBuffer = await this._playbackCtx.decodeAudioData(arrayBuffer);\n\n // Apply fade-in to avoid click artifacts\n this._applyFadeIn(audioBuffer);\n\n this._pendingDecodes--;\n\n // If playback was stopped while decoding, discard\n if (!this._isPlaying) return;\n\n // If nothing is currently playing, start immediately\n if (!this._currentSource) {\n this._playBuffer(audioBuffer);\n }\n // Otherwise the onended chain will pick it up from the queue\n } catch {\n this._pendingDecodes--;\n this._checkPlaybackComplete();\n }\n }\n\n private _applyFadeIn(audioBuffer: AudioBuffer): void {\n const fadeLen = Math.min(FADE_IN_SAMPLES, audioBuffer.length);\n for (let ch = 0; ch < audioBuffer.numberOfChannels; ch++) {\n const data = audioBuffer.getChannelData(ch);\n for (let i = 0; i < fadeLen; i++) {\n data[i] *= i / fadeLen;\n }\n }\n }\n\n private _playBuffer(audioBuffer: AudioBuffer): void {\n if (!this._playbackCtx) return;\n\n const source = this._playbackCtx.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(this._playbackCtx.destination);\n\n source.onended = () => {\n this._currentSource = null;\n this._playNextChunk();\n };\n\n this._currentSource = source;\n source.start();\n }\n\n private _playNextChunk(): void {\n // Remove the first item (already played or being decoded)\n if (this._playbackQueue.length > 0) {\n this._playbackQueue.shift();\n }\n\n // Check if there are decoded buffers ready\n // Since we decode in order and chain via onended, the next chunk should\n // already be decoded. We re-decode from the queue for simplicity.\n if (this._playbackQueue.length > 0) {\n const nextData = this._playbackQueue[0];\n this._pendingDecodes++;\n this._decodeAndEnqueue(nextData);\n } else {\n this._checkPlaybackComplete();\n }\n }\n\n private _checkPlaybackComplete(): void {\n if (this._playbackQueue.length === 0 && this._pendingDecodes <= 0) {\n this._isPlaying = false;\n this.onPlaybackComplete?.();\n }\n }\n}\n","// ---------------------------------------------------------------------------\n// VocallClient - WebSocket client for the Vocall AAP protocol (React edition)\n// ---------------------------------------------------------------------------\n// Unlike the Angular version (RxJS BehaviorSubjects), this client uses a\n// simple callback-based change notification pattern suited for React hooks.\n// ---------------------------------------------------------------------------\n\nimport {\n ActionResult,\n ChatMessage,\n ChatMessageOut,\n ChatRole,\n ChatTokenOut,\n CommandMessage,\n ConfigResponse,\n ErrorOut,\n ManifestMessage,\n ResultMessage,\n StateMessage,\n StatusOut,\n UIAction,\n VocallStatus,\n VoiceState,\n} from '../protocol/types';\nimport { WebVoiceService } from '../voice/web-voice-service';\n\n/** Target sample rate for the voice protocol. */\nconst TARGET_SAMPLE_RATE = 16000;\n\n// ---------------------------------------------------------------------------\n// Field registry types\n// ---------------------------------------------------------------------------\n\nexport interface FieldEntry {\n /** The HTML input/select/textarea element */\n element: HTMLElement;\n /** Setter to update the value programmatically (triggers change detection) */\n setValue: (value: string) => void;\n /** Getter for the current value */\n getValue: () => string;\n}\n\nexport type ActionCallback = () => void | Promise<void>;\nexport type NavigateCallback = (screenId: string) => void;\nexport type ModalOpenCallback = (modalId: string, query?: string) => void;\nexport type ModalCloseCallback = () => void;\nexport type ToastCallback = (message: string, level: string, duration?: number) => void;\nexport type ConfirmCallback = (seq: number, message: string) => void;\n\n// ---------------------------------------------------------------------------\n// Change listener type\n// ---------------------------------------------------------------------------\n\nexport type VocallChangeListener = () => void;\n\n// ---------------------------------------------------------------------------\n// VocallClient\n// ---------------------------------------------------------------------------\n\nexport class VocallClient {\n // -----------------------------------------------------------------------\n // Configuration\n // -----------------------------------------------------------------------\n\n readonly serverUrl: string;\n token?: string;\n private _visitorId: string;\n\n get visitorId(): string {\n return this._visitorId;\n }\n set visitorId(id: string) {\n this._visitorId = id;\n }\n\n // -----------------------------------------------------------------------\n // Observable state (getters + onChange)\n // -----------------------------------------------------------------------\n\n private _status: VocallStatus = VocallStatus.Disconnected;\n get status(): VocallStatus {\n return this._status;\n }\n\n private _connected = false;\n get connected(): boolean {\n return this._connected;\n }\n\n private _messages: ChatMessage[] = [];\n get messages(): readonly ChatMessage[] {\n return this._messages;\n }\n\n private _sessionId: string | null = null;\n get sessionId(): string | null {\n return this._sessionId;\n }\n\n // -----------------------------------------------------------------------\n // Voice state (public getters)\n // -----------------------------------------------------------------------\n\n private _voiceEnabled = false;\n get voiceEnabled(): boolean {\n return this._voiceEnabled;\n }\n\n /** Whether the platform supports voice capture (Web Audio API). */\n get voiceSupported(): boolean {\n return this._getVoice().isSupported;\n }\n\n private _voiceState: VoiceState = 'idle';\n get voiceState(): VoiceState {\n return this._voiceState;\n }\n\n private _recording = false;\n get recording(): boolean {\n return this._recording;\n }\n\n private _partialTranscription: string | null = null;\n get partialTranscription(): string | null {\n return this._partialTranscription;\n }\n\n private _audioLevel = 0;\n get audioLevel(): number {\n return this._audioLevel;\n }\n\n // -----------------------------------------------------------------------\n // Voice internals\n // -----------------------------------------------------------------------\n\n private _voiceWs: WebSocket | null = null;\n private _alwaysListening = false;\n private _ttsActive = false;\n private _ttsEndReceived = false;\n private _llmDone = false;\n private _pendingTtsChunks = 0;\n private _voice: WebVoiceService | null = null;\n\n private _getVoice(): WebVoiceService {\n if (!this._voice) {\n this._voice = new WebVoiceService();\n this._voice.onAudioLevel = (level: number) => {\n this._audioLevel = level;\n this._notify();\n };\n this._voice.onPlaybackComplete = () => {\n this._onPlaybackComplete();\n };\n }\n return this._voice;\n }\n\n // -----------------------------------------------------------------------\n // Change notification (React hooks subscribe here)\n // -----------------------------------------------------------------------\n\n private _listeners = new Set<VocallChangeListener>();\n\n subscribe(listener: VocallChangeListener): () => void {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n private _notify(): void {\n for (const listener of this._listeners) {\n listener();\n }\n }\n\n // -----------------------------------------------------------------------\n // Callbacks (set by the host app)\n // -----------------------------------------------------------------------\n\n onNavigate: NavigateCallback | null = null;\n onToast: ToastCallback | null = null;\n onConfirm: ConfirmCallback | null = null;\n onOpenModal: ModalOpenCallback | null = null;\n onCloseModal: ModalCloseCallback | null = null;\n\n // -----------------------------------------------------------------------\n // Field & action registries\n // -----------------------------------------------------------------------\n\n private _fields = new Map<string, Map<string, FieldEntry>>();\n private _actions = new Map<string, Map<string, ActionCallback>>();\n\n // -----------------------------------------------------------------------\n // Internal state\n // -----------------------------------------------------------------------\n\n private _ws: WebSocket | null = null;\n private _manifest: ManifestMessage | null = null;\n private _tokenBuffer = '';\n private _streamingMessage: ChatMessage | null = null;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _reconnectAttempts = 0;\n private _intentionalDisconnect = false;\n private _pendingConfirmSeq = -1;\n\n private static readonly MAX_RECONNECT_ATTEMPTS = 10;\n private static readonly RETRY_DELAY_MS = 300;\n private static readonly MAX_RETRIES = 3;\n\n // Sequential actions that must not run in parallel\n private static readonly SEQUENTIAL_ACTIONS = new Set([\n 'navigate', 'click', 'open_modal', 'close_modal', 'ask_confirm', 'show_toast',\n ]);\n\n // -----------------------------------------------------------------------\n // Constructor\n // -----------------------------------------------------------------------\n\n constructor(serverUrl: string, options?: { token?: string; visitorId?: string }) {\n this.serverUrl = serverUrl;\n this.token = options?.token;\n this._visitorId = options?.visitorId ?? VocallClient._loadOrCreateVisitorId();\n }\n\n // -----------------------------------------------------------------------\n // Visitor ID persistence\n // -----------------------------------------------------------------------\n\n private static _generateUuid(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback UUID v4\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private static _loadOrCreateVisitorId(): string {\n const STORAGE_KEY = 'vocall_visitor_id';\n try {\n const existing = localStorage.getItem(STORAGE_KEY);\n if (existing) return existing;\n const id = VocallClient._generateUuid();\n localStorage.setItem(STORAGE_KEY, id);\n return id;\n } catch {\n return VocallClient._generateUuid();\n }\n }\n\n // -----------------------------------------------------------------------\n // Field & action registration\n // -----------------------------------------------------------------------\n\n registerField(screenId: string, fieldId: string, entry: FieldEntry): void {\n if (!this._fields.has(screenId)) {\n this._fields.set(screenId, new Map());\n }\n this._fields.get(screenId)!.set(fieldId, entry);\n }\n\n unregisterField(screenId: string, fieldId: string): void {\n this._fields.get(screenId)?.delete(fieldId);\n if (this._fields.get(screenId)?.size === 0) {\n this._fields.delete(screenId);\n }\n }\n\n unregisterScreen(screenId: string): void {\n this._fields.delete(screenId);\n this._actions.delete(screenId);\n }\n\n registerAction(screenId: string, actionId: string, callback: ActionCallback): void {\n if (!this._actions.has(screenId)) {\n this._actions.set(screenId, new Map());\n }\n this._actions.get(screenId)!.set(actionId, callback);\n }\n\n unregisterAction(screenId: string, actionId: string): void {\n this._actions.get(screenId)?.delete(actionId);\n }\n\n findField(fieldId: string): FieldEntry | undefined {\n for (const screen of this._fields.values()) {\n if (screen.has(fieldId)) return screen.get(fieldId);\n }\n return undefined;\n }\n\n findAction(actionId: string): ActionCallback | undefined {\n for (const screen of this._actions.values()) {\n if (screen.has(actionId)) return screen.get(actionId);\n }\n return undefined;\n }\n\n // -----------------------------------------------------------------------\n // Connection\n // -----------------------------------------------------------------------\n\n connect(manifest: ManifestMessage): void {\n this._manifest = manifest;\n this._intentionalDisconnect = false;\n this._doConnect();\n }\n\n private _doConnect(): void {\n this.disconnect(false);\n\n const url = new URL(this.serverUrl);\n url.searchParams.set('visitor_id', this._visitorId);\n if (this.token) {\n url.searchParams.set('token', this.token);\n }\n\n try {\n this._ws = new WebSocket(url.toString());\n\n this._ws.onmessage = (event: MessageEvent) => {\n this._onMessage(event.data);\n };\n\n this._ws.onclose = () => {\n this._onDone();\n };\n\n this._ws.onerror = () => {\n this._onError();\n };\n } catch {\n this._scheduleReconnect();\n }\n }\n\n disconnect(intentional = true): void {\n if (intentional) this._intentionalDisconnect = true;\n\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n\n // Close voice WS and reset voice state\n this._closeVoiceWs();\n if (this._voice) {\n this._voice.stopCapture();\n this._voice.stopPlayback();\n }\n this._voiceState = 'idle';\n this._recording = false;\n this._partialTranscription = null;\n this._audioLevel = 0;\n this._alwaysListening = false;\n this._resetTtsState();\n\n if (this._ws) {\n this._ws.onclose = null;\n this._ws.onerror = null;\n this._ws.onmessage = null;\n this._ws.close();\n this._ws = null;\n }\n\n this._sessionId = null;\n this._tokenBuffer = '';\n this._streamingMessage = null;\n this._pendingConfirmSeq = -1;\n this._status = VocallStatus.Disconnected;\n this._connected = false;\n this._notify();\n }\n\n // -----------------------------------------------------------------------\n // Sending messages\n // -----------------------------------------------------------------------\n\n sendText(text: string): void {\n if (!text.trim() || !this._ws) return;\n\n this._addMessage({ role: ChatRole.User, text, timestamp: new Date() });\n this._wsSend({ type: 'text', message: text });\n }\n\n sendConfirm(seq: number, confirmed: boolean): void {\n this._wsSend({ type: 'confirm', seq, confirmed });\n this._pendingConfirmSeq = -1;\n }\n\n sendResult(seq: number, results: ActionResult[], state?: StateMessage): void {\n const msg: ResultMessage = { type: 'result', seq, results };\n if (state) msg.state = state;\n this._wsSend(msg);\n }\n\n sendState(state: StateMessage): void {\n this._wsSend(state);\n }\n\n private _wsSend(obj: unknown): void {\n if (this._ws && this._ws.readyState === WebSocket.OPEN) {\n this._ws.send(JSON.stringify(obj));\n }\n }\n\n // -----------------------------------------------------------------------\n // Incoming message handling\n // -----------------------------------------------------------------------\n\n private _onMessage(raw: string): void {\n let json: Record<string, unknown>;\n try {\n json = JSON.parse(raw);\n } catch {\n return;\n }\n\n const type = json['type'] as string | undefined;\n if (!type) return;\n\n switch (type) {\n case 'config':\n this._handleConfig(json as unknown as ConfigResponse);\n break;\n case 'chat':\n this._handleChat(json as unknown as ChatMessageOut);\n break;\n case 'chat_token':\n this._handleChatToken(json as unknown as ChatTokenOut);\n break;\n case 'chat_end':\n this._flushTokenBuffer();\n break;\n case 'status':\n this._handleStatus(json as unknown as StatusOut);\n break;\n case 'command':\n this._handleCommand(json as unknown as CommandMessage);\n break;\n case 'error':\n this._handleError(json as unknown as ErrorOut);\n break;\n case 'history':\n this._handleHistory(json);\n break;\n }\n }\n\n private _handleConfig(config: ConfigResponse): void {\n this._sessionId = config.sessionId;\n this._status = VocallStatus.Idle;\n this._connected = true;\n this._reconnectAttempts = 0;\n\n // Extract voice feature flag\n this._voiceEnabled = config.features?.voice === true;\n\n this._notify();\n\n // Send manifest right after receiving config\n if (this._manifest) {\n this._wsSend(this._manifest);\n }\n }\n\n private _handleHistory(json: Record<string, unknown>): void {\n const items = json['messages'] as Array<Record<string, unknown>> | undefined;\n if (!items || !items.length) return;\n\n this._messages = [];\n for (const item of items) {\n const role = item['role'] as string;\n const content = item['content'] as string;\n if (!content) continue;\n const chatRole = role === 'user' ? ChatRole.User : role === 'assistant' ? ChatRole.Agent : null;\n if (!chatRole) continue;\n this._messages.push({ role: chatRole, text: content, timestamp: new Date() });\n }\n this._notify();\n }\n\n private _handleChat(chat: ChatMessageOut): void {\n // Flush any pending token buffer first\n this._flushTokenBuffer();\n\n this._addMessage({\n role: ChatRole.Agent,\n text: chat.message,\n timestamp: new Date(),\n });\n }\n\n private _handleChatToken(token: ChatTokenOut): void {\n this._tokenBuffer += token.token;\n\n if (this._streamingMessage) {\n // Update existing streaming message\n this._streamingMessage.text = this._tokenBuffer;\n } else {\n // Create new streaming message\n this._streamingMessage = {\n role: ChatRole.Agent,\n text: this._tokenBuffer,\n timestamp: new Date(),\n };\n this._messages.push(this._streamingMessage);\n }\n\n this._notify();\n }\n\n private _flushTokenBuffer(): void {\n this._tokenBuffer = '';\n this._streamingMessage = null;\n }\n\n private _handleStatus(status: StatusOut): void {\n this._status = this._mapStatus(status.status);\n this._notify();\n }\n\n private _mapStatus(status: string): VocallStatus {\n switch (status) {\n case 'idle': return VocallStatus.Idle;\n case 'listening': return VocallStatus.Listening;\n case 'recording': return VocallStatus.Recording;\n case 'thinking': return VocallStatus.Thinking;\n case 'speaking': return VocallStatus.Speaking;\n case 'executing': return VocallStatus.Executing;\n default: return VocallStatus.Idle;\n }\n }\n\n private _handleError(error: ErrorOut): void {\n this._addMessage({\n role: ChatRole.System,\n text: error.message,\n timestamp: new Date(),\n });\n this._status = VocallStatus.Idle;\n this._notify();\n }\n\n // -----------------------------------------------------------------------\n // Command execution\n // -----------------------------------------------------------------------\n\n private async _handleCommand(cmd: CommandMessage): Promise<void> {\n const results: ActionResult[] = new Array(cmd.actions.length);\n\n // Separate actions into sequential and parallel groups\n const sequentialIndices: number[] = [];\n const parallelIndices: number[] = [];\n\n for (let i = 0; i < cmd.actions.length; i++) {\n if (VocallClient.SEQUENTIAL_ACTIONS.has(cmd.actions[i].do)) {\n sequentialIndices.push(i);\n } else {\n parallelIndices.push(i);\n }\n }\n\n // Run sequential actions first (in order)\n for (const i of sequentialIndices) {\n try {\n await this._executeAction(cmd.actions[i], cmd.seq);\n results[i] = { index: i, success: true };\n } catch (e: unknown) {\n results[i] = { index: i, success: false, error: String(e) };\n }\n }\n\n // Run parallel actions concurrently\n if (parallelIndices.length > 0) {\n await Promise.all(\n parallelIndices.map(async (i) => {\n try {\n await this._executeAction(cmd.actions[i], cmd.seq);\n results[i] = { index: i, success: true };\n } catch (e: unknown) {\n results[i] = { index: i, success: false, error: String(e) };\n }\n }),\n );\n }\n\n // Fill any gaps\n for (let i = 0; i < results.length; i++) {\n if (!results[i]) {\n results[i] = { index: i, success: false, error: 'not executed' };\n }\n }\n\n this.sendResult(cmd.seq, results);\n }\n\n private async _executeAction(action: UIAction, seq: number): Promise<void> {\n switch (action.do) {\n case 'navigate':\n await this._execNavigate(action);\n break;\n case 'fill':\n await this._execFill(action);\n break;\n case 'clear':\n this._execClear(action);\n break;\n case 'select':\n this._execSelect(action);\n break;\n case 'click':\n await this._execClick(action);\n break;\n case 'focus':\n this._execFocus(action);\n break;\n case 'highlight':\n this._execHighlight(action);\n break;\n case 'scroll_to':\n this._execScrollTo(action);\n break;\n case 'show_toast':\n this._execShowToast(action);\n break;\n case 'ask_confirm':\n this._execAskConfirm(action, seq);\n break;\n case 'open_modal':\n this._execOpenModal(action);\n break;\n case 'close_modal':\n this._execCloseModal();\n break;\n case 'enable':\n this._execEnable(action);\n break;\n case 'disable':\n this._execDisable(action);\n break;\n }\n }\n\n private async _execNavigate(action: UIAction): Promise<void> {\n const screenId = action.screen;\n if (!screenId) return;\n this.onNavigate?.(screenId);\n await this._sleep(VocallClient.RETRY_DELAY_MS);\n }\n\n private async _execFill(action: UIAction): Promise<void> {\n const fieldId = action.field;\n if (!fieldId) return;\n\n let entry = this.findField(fieldId);\n // Retry - field may not be registered yet\n for (let i = 0; i < VocallClient.MAX_RETRIES && !entry; i++) {\n await this._sleep(VocallClient.RETRY_DELAY_MS);\n entry = this.findField(fieldId);\n }\n if (!entry) {\n throw new Error(`field \"${fieldId}\" not registered`);\n }\n\n const value = action.value != null ? String(action.value) : '';\n const animate = action.animate ?? 'typewriter';\n\n if (animate === 'typewriter' && value.length > 1) {\n await this._typewriterFill(entry, value, action.speed ?? 40);\n } else {\n entry.setValue(value);\n }\n }\n\n private async _typewriterFill(entry: FieldEntry, value: string, speedMs: number): Promise<void> {\n entry.element.classList.add('filling');\n entry.element.focus();\n entry.setValue('');\n\n for (let i = 0; i < value.length; i++) {\n entry.setValue(value.substring(0, i + 1));\n await this._sleep(speedMs);\n }\n\n entry.element.classList.remove('filling');\n }\n\n private _execClear(action: UIAction): void {\n const fieldId = action.field;\n if (!fieldId) return;\n const entry = this.findField(fieldId);\n if (entry) entry.setValue('');\n }\n\n private _execSelect(action: UIAction): void {\n const fieldId = action.field;\n if (!fieldId) return;\n const entry = this.findField(fieldId);\n if (entry) entry.setValue(String(action.value ?? ''));\n }\n\n private async _execClick(action: UIAction): Promise<void> {\n const actionId = action.action;\n if (!actionId) return;\n\n let callback = this.findAction(actionId);\n for (let i = 0; i < VocallClient.MAX_RETRIES && !callback; i++) {\n await this._sleep(VocallClient.RETRY_DELAY_MS);\n callback = this.findAction(actionId);\n }\n if (!callback) {\n throw new Error(`action \"${actionId}\" not registered`);\n }\n await callback();\n }\n\n private _execFocus(action: UIAction): void {\n const fieldId = action.field;\n if (!fieldId) return;\n const entry = this.findField(fieldId);\n if (entry) entry.element.focus();\n }\n\n private _execHighlight(action: UIAction): void {\n const fieldId = action.field;\n if (!fieldId) return;\n const entry = this.findField(fieldId);\n if (!entry) return;\n\n entry.element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n entry.element.classList.add('highlighted');\n const duration = action.duration ?? 2000;\n setTimeout(() => entry.element.classList.remove('highlighted'), duration);\n }\n\n private _execScrollTo(action: UIAction): void {\n const fieldId = action.field;\n if (!fieldId) return;\n const entry = this.findField(fieldId);\n if (entry) entry.element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n private _execShowToast(action: UIAction): void {\n const message = action.message ?? '';\n const level = action.level ?? 'info';\n const duration = action.duration;\n this.onToast?.(message, level, duration);\n }\n\n private _execAskConfirm(action: UIAction, seq: number): void {\n this._pendingConfirmSeq = seq;\n const message = action.message ?? 'Confirmar?';\n this.onConfirm?.(seq, message);\n }\n\n private _execOpenModal(action: UIAction): void {\n const modalId = action.modal;\n if (!modalId) return;\n this.onOpenModal?.(modalId, action.query);\n }\n\n private _execCloseModal(): void {\n this.onCloseModal?.();\n }\n\n private _execEnable(action: UIAction): void {\n const entry = action.field ? this.findField(action.field) : undefined;\n if (entry && entry.element instanceof HTMLInputElement) {\n entry.element.disabled = false;\n }\n }\n\n private _execDisable(action: UIAction): void {\n const entry = action.field ? this.findField(action.field) : undefined;\n if (entry && entry.element instanceof HTMLInputElement) {\n entry.element.disabled = true;\n }\n }\n\n // -----------------------------------------------------------------------\n // Voice: public methods\n // -----------------------------------------------------------------------\n\n async startAlwaysListening(): Promise<void> {\n if (!this._getVoice().isSupported || !this._connected) return;\n\n this._alwaysListening = true;\n await this._openVoiceWs(false);\n const voice = this._getVoice();\n await voice.startCapture((chunk: Uint8Array) => {\n if (this._voiceWs && this._voiceWs.readyState === WebSocket.OPEN) {\n this._voiceWs.send(chunk);\n }\n });\n this._voiceState = 'listening';\n this._recording = false;\n this._notify();\n }\n\n stopAlwaysListening(): void {\n this._alwaysListening = false;\n const voice = this._getVoice();\n voice.stopCapture();\n voice.stopPlayback();\n this._closeVoiceWs();\n this._voiceState = 'idle';\n this._recording = false;\n this._partialTranscription = null;\n this._audioLevel = 0;\n this._resetTtsState();\n this._notify();\n }\n\n async startRecording(): Promise<void> {\n if (!this._getVoice().isSupported || !this._connected) return;\n\n this._alwaysListening = false;\n await this._openVoiceWs(true);\n const voice = this._getVoice();\n await voice.startCapture((chunk: Uint8Array) => {\n if (this._voiceWs && this._voiceWs.readyState === WebSocket.OPEN) {\n this._voiceWs.send(chunk);\n }\n });\n this._voiceState = 'recording';\n this._recording = true;\n this._notify();\n }\n\n stopRecording(): void {\n const voice = this._getVoice();\n voice.stopCapture();\n this._recording = false;\n this._voiceState = 'thinking';\n this._notify();\n\n // Send EOF to signal end of audio\n if (this._voiceWs && this._voiceWs.readyState === WebSocket.OPEN) {\n this._voiceWs.send(JSON.stringify({ type: 'eof' }));\n }\n }\n\n interrupt(): void {\n const voice = this._getVoice();\n voice.stopPlayback();\n this._ttsActive = false;\n\n if (this._voiceWs && this._voiceWs.readyState === WebSocket.OPEN) {\n this._voiceWs.send(JSON.stringify({ type: 'interrupt' }));\n this._voiceWs.send(JSON.stringify({ type: 'tts_state', active: false }));\n }\n\n if (this._alwaysListening) {\n voice.muteMic(false);\n this._voiceState = 'listening';\n } else {\n this._voiceState = 'idle';\n }\n this._partialTranscription = null;\n this._resetTtsState();\n this._notify();\n }\n\n // -----------------------------------------------------------------------\n // Voice: private WS management\n // -----------------------------------------------------------------------\n\n private async _openVoiceWs(directMode: boolean): Promise<void> {\n this._closeVoiceWs();\n\n // Derive voice WS URL from serverUrl, replacing path with /ws/stream\n const url = new URL(this.serverUrl);\n url.pathname = '/ws/stream';\n url.searchParams.set('visitor_id', this._visitorId);\n if (this.token) {\n url.searchParams.set('token', this.token);\n }\n\n return new Promise<void>((resolve, reject) => {\n try {\n this._voiceWs = new WebSocket(url.toString());\n this._voiceWs.binaryType = 'arraybuffer';\n\n this._voiceWs.onopen = () => {\n // Send config message\n const config: Record<string, unknown> = {\n type: 'config',\n sample_rate: TARGET_SAMPLE_RATE,\n };\n if (directMode) {\n config['mode'] = 'direct';\n }\n this._voiceWs!.send(JSON.stringify(config));\n resolve();\n };\n\n this._voiceWs.onmessage = (event: MessageEvent) => {\n this._onVoiceStreamMessage(event);\n };\n\n this._voiceWs.onclose = () => {\n this._voiceWs = null;\n };\n\n this._voiceWs.onerror = () => {\n this._voiceWs = null;\n reject(new Error('Voice WebSocket connection failed'));\n };\n } catch (e) {\n reject(e);\n }\n });\n }\n\n private _closeVoiceWs(): void {\n if (this._voiceWs) {\n this._voiceWs.onclose = null;\n this._voiceWs.onerror = null;\n this._voiceWs.onmessage = null;\n this._voiceWs.close();\n this._voiceWs = null;\n }\n }\n\n private _resetTtsState(): void {\n this._ttsActive = false;\n this._ttsEndReceived = false;\n this._llmDone = false;\n this._pendingTtsChunks = 0;\n }\n\n // -----------------------------------------------------------------------\n // Voice: stream message handler\n // -----------------------------------------------------------------------\n\n private _onVoiceStreamMessage(event: MessageEvent): void {\n const { data } = event;\n\n // Binary frame = WAV audio from TTS\n if (data instanceof ArrayBuffer) {\n const voice = this._getVoice();\n voice.playAudio(new Uint8Array(data));\n return;\n }\n\n // JSON text frame\n let json: Record<string, unknown>;\n try {\n json = JSON.parse(data as string);\n } catch {\n return;\n }\n\n const type = json['type'] as string | undefined;\n if (!type) return;\n\n switch (type) {\n case 'wake_word':\n this._voiceState = 'recording';\n this._recording = true;\n this._partialTranscription = null;\n this._notify();\n break;\n\n case 'partial':\n this._partialTranscription = (json['text'] as string) ?? null;\n this._notify();\n break;\n\n case 'transcription':\n this._partialTranscription = null;\n this._voiceState = 'thinking';\n this._recording = false;\n this._notify();\n break;\n\n case 'llm_start':\n this._llmDone = false;\n break;\n\n case 'llm_token': {\n const text = json['text'] as string | undefined;\n if (text) {\n // Accumulate LLM tokens into the chat message stream\n this._tokenBuffer += text;\n if (this._streamingMessage) {\n this._streamingMessage.text = this._tokenBuffer;\n } else {\n this._streamingMessage = {\n role: ChatRole.Agent,\n text: this._tokenBuffer,\n timestamp: new Date(),\n };\n this._messages = [...this._messages, this._streamingMessage];\n }\n this._notify();\n }\n break;\n }\n\n case 'llm_end':\n this._llmDone = true;\n this._flushTokenBuffer();\n this._ttsEndReceived = this._pendingTtsChunks <= 0 && this._llmDone;\n break;\n\n case 'tts_start':\n this._pendingTtsChunks++;\n this._ttsActive = true;\n this._ttsEndReceived = false;\n this._voiceState = 'speaking';\n // Echo suppression: mute mic during TTS\n this._getVoice().muteMic(true);\n this._notify();\n break;\n\n case 'tts_end':\n this._pendingTtsChunks--;\n if (this._pendingTtsChunks <= 0 && this._llmDone) {\n this._ttsEndReceived = true;\n }\n break;\n\n case 'interrupted':\n this._getVoice().stopPlayback();\n this._ttsActive = false;\n this._resetTtsState();\n if (this._alwaysListening) {\n this._getVoice().muteMic(false);\n this._voiceState = 'listening';\n } else {\n this._voiceState = 'idle';\n }\n this._partialTranscription = null;\n this._notify();\n break;\n\n case 'conversation_end':\n this._getVoice().stopPlayback();\n this._ttsActive = false;\n this._resetTtsState();\n if (this._alwaysListening) {\n this._getVoice().muteMic(false);\n this._voiceState = 'listening';\n } else {\n this._voiceState = 'idle';\n this._closeVoiceWs();\n this._getVoice().stopCapture();\n this._recording = false;\n }\n this._partialTranscription = null;\n this._notify();\n break;\n }\n }\n\n // -----------------------------------------------------------------------\n // Voice: playback completion handler\n // -----------------------------------------------------------------------\n\n private _onPlaybackComplete(): void {\n if (this._alwaysListening) {\n // Guard: only finalize when all TTS chunks have been received\n if (!this._ttsEndReceived) return;\n this._finalizeTts();\n } else {\n // In direct mode, playback complete without always-listening\n // means we just go idle (conversation_end handles the real end)\n }\n }\n\n private _finalizeTts(): void {\n this._ttsActive = false;\n\n // Notify server that TTS playback is done\n if (this._voiceWs && this._voiceWs.readyState === WebSocket.OPEN) {\n this._voiceWs.send(JSON.stringify({ type: 'tts_state', active: false }));\n }\n\n // Unmute mic for next utterance\n this._getVoice().muteMic(false);\n\n this._voiceState = 'listening';\n this._recording = false;\n this._partialTranscription = null;\n this._resetTtsState();\n this._notify();\n }\n\n // -----------------------------------------------------------------------\n // Message management\n // -----------------------------------------------------------------------\n\n private _addMessage(msg: ChatMessage): void {\n this._messages = [...this._messages, msg];\n this._notify();\n }\n\n clearMessages(): void {\n this._messages = [];\n this._tokenBuffer = '';\n this._streamingMessage = null;\n this._notify();\n }\n\n // -----------------------------------------------------------------------\n // Reconnection\n // -----------------------------------------------------------------------\n\n private _onDone(): void {\n this._connected = false;\n this._ws = null;\n\n if (this._intentionalDisconnect) {\n this._notify();\n return;\n }\n\n this._status = VocallStatus.Disconnected;\n this._notify();\n this._scheduleReconnect();\n }\n\n private _onError(): void {\n if (this._intentionalDisconnect) return;\n this._status = VocallStatus.Disconnected;\n this._connected = false;\n this._notify();\n this._scheduleReconnect();\n }\n\n private _scheduleReconnect(): void {\n if (this._intentionalDisconnect) return;\n if (this._reconnectAttempts >= VocallClient.MAX_RECONNECT_ATTEMPTS) return;\n\n if (this._reconnectTimer) {\n clearTimeout(this._reconnectTimer);\n }\n\n const delaySec = Math.min(this._reconnectAttempts + 1, 15);\n this._reconnectAttempts++;\n this._reconnectTimer = setTimeout(() => this._doConnect(), delaySec * 1000);\n }\n\n // -----------------------------------------------------------------------\n // Helpers\n // -----------------------------------------------------------------------\n\n private _sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // -----------------------------------------------------------------------\n // Dispose\n // -----------------------------------------------------------------------\n\n destroy(): void {\n this.disconnect(true);\n if (this._voice) {\n this._voice.dispose();\n this._voice = null;\n }\n this._listeners.clear();\n }\n}\n","// ---------------------------------------------------------------------------\n// VocallProvider - React Context provider for VocallClient\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';\nimport { VocallClient } from '../client/vocall-client';\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nconst VocallContext = createContext<VocallClient | null>(null);\n\n// ---------------------------------------------------------------------------\n// Provider props\n// ---------------------------------------------------------------------------\n\nexport interface VocallProviderProps {\n serverUrl: string;\n token?: string;\n visitorId?: string;\n children: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport function VocallProvider({ serverUrl, token, visitorId, children }: VocallProviderProps) {\n // Keep the client stable across re-renders (only recreate if serverUrl changes)\n const clientRef = useRef<VocallClient | null>(null);\n\n const client = useMemo(() => {\n // Destroy previous client if any\n clientRef.current?.destroy();\n const c = new VocallClient(serverUrl, { token, visitorId });\n clientRef.current = c;\n return c;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [serverUrl]);\n\n // Update token synchronously so children see the new value during render\n client.token = token;\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n clientRef.current?.destroy();\n clientRef.current = null;\n };\n }, []);\n\n return (\n <VocallContext.Provider value={client}>\n {children}\n </VocallContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Hook to consume context\n// ---------------------------------------------------------------------------\n\nexport function useVocallClient(): VocallClient {\n const client = useContext(VocallContext);\n if (!client) {\n throw new Error('useVocallClient must be used within a <VocallProvider>');\n }\n return client;\n}\n","// ---------------------------------------------------------------------------\n// useVocall - Main React hook for the Vocall client\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\nimport { useVocallClient } from '../context/vocall-provider';\nimport { ManifestMessage, ChatMessage, VocallStatus, VoiceState } from '../protocol/types';\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface UseVocallReturn {\n client: ReturnType<typeof useVocallClient>;\n status: VocallStatus;\n messages: readonly ChatMessage[];\n connected: boolean;\n sessionId: string | null;\n voiceEnabled: boolean;\n voiceSupported: boolean;\n voiceState: VoiceState;\n recording: boolean;\n partialTranscription: string | null;\n sendText: (text: string) => void;\n connect: (manifest: ManifestMessage) => void;\n disconnect: () => void;\n clearMessages: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useVocall(): UseVocallReturn {\n const client = useVocallClient();\n\n // useSyncExternalStore for tear-free reads from the client\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(onStoreChange),\n [client],\n );\n\n const status = useSyncExternalStore(\n subscribe,\n () => client.status,\n () => VocallStatus.Disconnected,\n );\n\n const connected = useSyncExternalStore(\n subscribe,\n () => client.connected,\n () => false,\n );\n\n const messages = useSyncExternalStore(\n subscribe,\n () => client.messages,\n () => [] as readonly ChatMessage[],\n );\n\n const sessionId = useSyncExternalStore(\n subscribe,\n () => client.sessionId,\n () => null,\n );\n\n const voiceEnabled = useSyncExternalStore(\n subscribe,\n () => client.voiceEnabled,\n () => false,\n );\n\n const voiceSupported = useSyncExternalStore(\n subscribe,\n () => client.voiceSupported,\n () => false,\n );\n\n const voiceState = useSyncExternalStore(\n subscribe,\n () => client.voiceState,\n () => 'idle' as VoiceState,\n );\n\n const recording = useSyncExternalStore(\n subscribe,\n () => client.recording,\n () => false,\n );\n\n const partialTranscription = useSyncExternalStore(\n subscribe,\n () => client.partialTranscription,\n () => null as string | null,\n );\n\n const sendText = useCallback(\n (text: string) => client.sendText(text),\n [client],\n );\n\n const connect = useCallback(\n (manifest: ManifestMessage) => client.connect(manifest),\n [client],\n );\n\n const disconnect = useCallback(\n () => client.disconnect(),\n [client],\n );\n\n const clearMessages = useCallback(\n () => client.clearMessages(),\n [client],\n );\n\n return {\n client,\n status,\n messages,\n connected,\n sessionId,\n voiceEnabled,\n voiceSupported,\n voiceState,\n recording,\n partialTranscription,\n sendText,\n connect,\n disconnect,\n clearMessages,\n };\n}\n","// ---------------------------------------------------------------------------\n// useVocallField - Field registration hook\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useVocallClient } from '../context/vocall-provider';\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface UseVocallFieldReturn {\n /** Attach to the field element via ref={ref} */\n ref: (element: HTMLElement | null) => void;\n /** Whether the field is currently registered with the client */\n registered: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useVocallField(\n screenId: string,\n fieldId: string,\n options?: {\n /** Custom setter (e.g. for React state-controlled inputs) */\n setValue?: (value: string) => void;\n /** Custom getter */\n getValue?: () => string;\n },\n): UseVocallFieldReturn {\n const client = useVocallClient();\n const [registered, setRegistered] = useState(false);\n const elementRef = useRef<HTMLElement | null>(null);\n\n const ref = useCallback(\n (element: HTMLElement | null) => {\n // Unregister previous\n if (elementRef.current && registered) {\n client.unregisterField(screenId, fieldId);\n setRegistered(false);\n }\n\n elementRef.current = element;\n\n if (element) {\n const isInput = element instanceof HTMLInputElement\n || element instanceof HTMLTextAreaElement\n || element instanceof HTMLSelectElement;\n\n client.registerField(screenId, fieldId, {\n element,\n setValue: options?.setValue ?? ((value: string) => {\n if (isInput) {\n (element as HTMLInputElement).value = value;\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }),\n getValue: options?.getValue ?? (() => {\n if (isInput) {\n return (element as HTMLInputElement).value;\n }\n return '';\n }),\n });\n setRegistered(true);\n }\n },\n [client, screenId, fieldId, options?.setValue, options?.getValue, registered],\n );\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n client.unregisterField(screenId, fieldId);\n };\n }, [client, screenId, fieldId]);\n\n return { ref, registered };\n}\n","// ---------------------------------------------------------------------------\n// useVocallAction - Action registration hook\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { useVocallClient } from '../context/vocall-provider';\nimport { ActionCallback } from '../client/vocall-client';\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Registers an action callback with the Vocall client.\n *\n * @param screenId - Screen that owns this action\n * @param actionId - Action identifier (matches manifest)\n * @param callback - Function invoked when the server sends a `click` command\n */\nexport function useVocallAction(\n screenId: string,\n actionId: string,\n callback: ActionCallback,\n): void {\n const client = useVocallClient();\n\n // Keep a stable ref to the latest callback so we don't re-register on every render\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n const handler: ActionCallback = () => callbackRef.current();\n client.registerAction(screenId, actionId, handler);\n\n return () => {\n client.unregisterAction(screenId, actionId);\n };\n }, [client, screenId, actionId]);\n}\n","// ---------------------------------------------------------------------------\n// useVocallVoice - React hook for voice functionality\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\nimport { useVocallClient } from '../context/vocall-provider';\n\nexport type VoiceState = 'idle' | 'listening' | 'recording' | 'thinking' | 'speaking';\n\nexport interface UseVocallVoiceReturn {\n /** Whether the server indicated voice is available for this session. */\n voiceEnabled: boolean;\n /** Current state of the voice state machine. */\n voiceState: VoiceState;\n /** Whether audio is currently being captured (direct mode). */\n recording: boolean;\n /** Partial transcription text from the server (live). */\n partialTranscription: string | null;\n /** Current microphone audio level (0-1). */\n audioLevel: number;\n\n /** Start always-listening mode (wake-word activated). */\n startAlwaysListening: () => Promise<void>;\n /** Stop always-listening mode. */\n stopAlwaysListening: () => void;\n /** Start direct recording (click-to-talk). */\n startRecording: () => Promise<void>;\n /** Stop direct recording and send EOF. */\n stopRecording: () => void;\n /** Interrupt TTS playback and notify the server. */\n interrupt: () => void;\n}\n\nexport function useVocallVoice(): UseVocallVoiceReturn {\n const client = useVocallClient();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => client.subscribe(onStoreChange),\n [client],\n );\n\n const voiceEnabled = useSyncExternalStore(\n subscribe,\n () => client.voiceEnabled,\n () => false,\n );\n\n const voiceState = useSyncExternalStore(\n subscribe,\n () => client.voiceState,\n () => 'idle' as VoiceState,\n );\n\n const recording = useSyncExternalStore(\n subscribe,\n () => client.recording,\n () => false,\n );\n\n const partialTranscription = useSyncExternalStore(\n subscribe,\n () => client.partialTranscription,\n () => null as string | null,\n );\n\n const audioLevel = useSyncExternalStore(\n subscribe,\n () => client.audioLevel,\n () => 0,\n );\n\n // Methods\n const startAlwaysListening = useCallback(\n () => client.startAlwaysListening(),\n [client],\n );\n\n const stopAlwaysListening = useCallback(\n () => client.stopAlwaysListening(),\n [client],\n );\n\n const startRecording = useCallback(\n () => client.startRecording(),\n [client],\n );\n\n const stopRecording = useCallback(\n () => client.stopRecording(),\n [client],\n );\n\n const interrupt = useCallback(\n () => client.interrupt(),\n [client],\n );\n\n return {\n voiceEnabled,\n voiceState,\n recording,\n partialTranscription,\n audioLevel,\n startAlwaysListening,\n stopAlwaysListening,\n startRecording,\n stopRecording,\n interrupt,\n };\n}\n","// ---------------------------------------------------------------------------\n// VocallChat - Chat panel component\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { useVocall } from '../hooks/use-vocall';\nimport { ChatRole, VocallStatus } from '../protocol/types';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface VocallChatProps {\n /** Whether the chat panel is visible */\n open: boolean;\n /** Called when the user requests to close the panel */\n onClose?: () => void;\n /** Assistant name displayed in the header */\n assistantName?: string;\n /** CSS class for the container */\n className?: string;\n /** Inline styles for the container */\n style?: React.CSSProperties;\n}\n\n// ---------------------------------------------------------------------------\n// Status labels\n// ---------------------------------------------------------------------------\n\nconst STATUS_LABELS: Record<string, string> = {\n thinking: 'Pensando...',\n executing: 'Executando...',\n speaking: 'Falando...',\n listening: 'Ouvindo...',\n recording: 'Gravando...',\n};\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function VocallChat({\n open,\n onClose,\n assistantName = 'Emma',\n className,\n style,\n}: VocallChatProps) {\n const { status, messages, connected, voiceSupported, recording, sendText, clearMessages, client } = useVocall();\n const [input, setInput] = useState('');\n const [micActive, setMicActive] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n // Auto-reset mic button when client stops recording\n useEffect(() => {\n if (micActive && !recording) {\n setMicActive(false);\n }\n }, [micActive, recording]);\n\n const toggleMic = useCallback(() => {\n if (micActive) {\n client.stopRecording();\n setMicActive(false);\n } else {\n client.startRecording();\n setMicActive(true);\n }\n }, [micActive, client]);\n\n // Auto-scroll to bottom on new messages\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n const handleSend = useCallback(() => {\n const text = input.trim();\n if (!text || !connected) return;\n sendText(text);\n setInput('');\n }, [input, connected, sendText]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n },\n [handleSend],\n );\n\n if (!open) return null;\n\n const statusLabel = STATUS_LABELS[status] || '';\n const statusClass = status !== VocallStatus.Idle ? status : '';\n\n return (\n <div\n className={`vocall-chat-panel ${className || ''}`}\n style={{\n position: 'fixed',\n bottom: 76,\n right: 16,\n width: 360,\n height: 560,\n background: '#fff',\n borderRadius: 16,\n boxShadow: '0 8px 32px rgba(0,0,0,0.15)',\n display: 'flex',\n flexDirection: 'column',\n zIndex: 999,\n overflow: 'hidden',\n ...style,\n }}\n >\n {/* Header */}\n <div\n className={`vocall-chat-header ${statusClass}`}\n style={{\n padding: '8px 12px',\n background: '#1E293B',\n borderRadius: '16px 16px 0 0',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n borderBottom: `2px solid ${statusClass ? _statusBorderColor(status) : 'transparent'}`,\n transition: 'border-color 0.3s',\n }}\n >\n <div\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: connected ? '#22C55E' : '#EF4444',\n boxShadow: connected ? '0 0 6px rgba(34,197,94,0.6)' : 'none',\n flexShrink: 0,\n }}\n />\n <span style={{ fontSize: 13, fontWeight: 600, color: '#fff' }}>{assistantName}</span>\n {statusLabel && (\n <>\n <span style={{ fontSize: 13, color: '#CBD5E1' }}>·</span>\n <span style={{ fontSize: 12, fontWeight: 500, color: _statusColor(status) }}>\n {statusLabel}\n </span>\n </>\n )}\n <div style={{ flex: 1 }} />\n <button\n onClick={clearMessages}\n title=\"Limpar\"\n style={{\n width: 28,\n height: 28,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n background: 'none',\n color: '#CBD5E1',\n cursor: 'pointer',\n borderRadius: 6,\n fontSize: 14,\n }}\n >\n &#128465;\n </button>\n <button\n onClick={onClose}\n title=\"Fechar\"\n style={{\n width: 28,\n height: 28,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n background: 'none',\n color: '#CBD5E1',\n cursor: 'pointer',\n borderRadius: 6,\n fontSize: 14,\n }}\n >\n &#x2715;\n </button>\n </div>\n\n {/* Messages */}\n <div\n style={{\n flex: 1,\n overflowY: 'auto',\n padding: 12,\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n }}\n >\n {messages.length === 0 ? (\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#94A3B8',\n gap: 8,\n }}\n >\n <span style={{ fontSize: 32 }}>&#128172;</span>\n <span style={{ fontSize: 13 }}>\n {connected ? `Diga algo para a ${assistantName}` : 'Conecte-se para conversar'}\n </span>\n </div>\n ) : (\n messages.map((msg, i) => (\n <div\n key={i}\n style={{\n display: 'flex',\n maxWidth: 280,\n alignSelf:\n msg.role === ChatRole.User\n ? 'flex-end'\n : msg.role === ChatRole.System\n ? 'center'\n : 'flex-start',\n }}\n >\n <div\n style={{\n padding: '8px 12px',\n borderRadius: 12,\n fontSize: 13,\n lineHeight: 1.5,\n wordBreak: 'break-word',\n ..._bubbleStyle(msg.role),\n }}\n >\n {msg.text}\n </div>\n </div>\n ))\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div\n style={{\n padding: '6px 8px 10px 12px',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n borderTop: '1px solid #E2E8F0',\n }}\n >\n <input\n type=\"text\"\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={!connected || micActive}\n placeholder={micActive ? 'Gravando... clique para parar' : 'Digite uma mensagem...'}\n style={{\n flex: 1,\n padding: '10px 12px',\n fontSize: 13,\n fontFamily: 'inherit',\n border: '1px solid #E2E8F0',\n borderRadius: 10,\n background: '#F8FAFC',\n color: micActive ? '#FF4060' : '#1E293B',\n outline: 'none',\n opacity: connected ? 1 : 0.5,\n }}\n />\n {voiceSupported && (\n <button\n onClick={toggleMic}\n disabled={!connected}\n style={{\n width: 36,\n height: 36,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n borderRadius: '50%',\n background: micActive ? '#FF4060' : '#3B82F6',\n color: '#fff',\n cursor: connected ? 'pointer' : 'not-allowed',\n flexShrink: 0,\n }}\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n {micActive ? (\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n ) : (\n <path d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5zm6 6c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z\"/>\n )}\n </svg>\n </button>\n )}\n <button\n onClick={handleSend}\n disabled={!connected || !input.trim() || micActive}\n style={{\n width: 36,\n height: 36,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n borderRadius: '50%',\n background: connected && input.trim() && !micActive ? '#3B82F6' : '#CBD5E1',\n color: '#fff',\n cursor: connected && input.trim() && !micActive ? 'pointer' : 'not-allowed',\n flexShrink: 0,\n fontSize: 16,\n }}\n >\n &#10148;\n </button>\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction _bubbleStyle(role: ChatRole): React.CSSProperties {\n switch (role) {\n case ChatRole.User:\n return { background: '#3B82F6', color: '#fff' };\n case ChatRole.Agent:\n return { background: '#F1F5F9', color: '#1E293B' };\n case ChatRole.System:\n return {\n background: 'rgba(245,158,11,0.08)',\n color: '#F59E0B',\n fontSize: 11,\n fontStyle: 'italic',\n textAlign: 'center',\n maxWidth: '100%',\n };\n }\n}\n\nfunction _statusBorderColor(status: VocallStatus): string {\n switch (status) {\n case VocallStatus.Thinking: return '#8B5CF6';\n case VocallStatus.Executing: return '#F59E0B';\n case VocallStatus.Speaking: return '#FFB347';\n case VocallStatus.Listening: return '#00D4FF';\n case VocallStatus.Recording: return '#FF4060';\n default: return 'transparent';\n }\n}\n\nfunction _statusColor(status: VocallStatus): string {\n switch (status) {\n case VocallStatus.Thinking: return '#8B5CF6';\n case VocallStatus.Executing: return '#F59E0B';\n case VocallStatus.Speaking: return '#FFB347';\n case VocallStatus.Listening: return '#00D4FF';\n case VocallStatus.Recording: return '#FF4060';\n default: return '#94A3B8';\n }\n}\n","// ---------------------------------------------------------------------------\n// VocallFab - Floating action button component\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport React from 'react';\nimport { useVocall } from '../hooks/use-vocall';\nimport { VocallStatus } from '../protocol/types';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface VocallFabProps {\n /** Called when the FAB is clicked */\n onClick?: () => void;\n /** CSS class */\n className?: string;\n /** Inline styles */\n style?: React.CSSProperties;\n}\n\n// ---------------------------------------------------------------------------\n// Color map\n// ---------------------------------------------------------------------------\n\nconst FAB_COLORS: Record<string, { bg: string; shadow: string }> = {\n disconnected: { bg: '#94A3B8', shadow: 'rgba(148,163,184,0.3)' },\n idle: { bg: '#3B82F6', shadow: 'rgba(59,130,246,0.35)' },\n thinking: { bg: '#8B5CF6', shadow: 'rgba(139,92,246,0.4)' },\n executing: { bg: '#F59E0B', shadow: 'rgba(245,158,11,0.4)' },\n speaking: { bg: '#FFB347', shadow: 'rgba(255,179,71,0.4)' },\n listening: { bg: '#00D4FF', shadow: 'rgba(0,212,255,0.4)' },\n recording: { bg: '#FF4060', shadow: 'rgba(255,64,96,0.4)' },\n};\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function VocallFab({ onClick, className, style }: VocallFabProps) {\n const { status, connected } = useVocall();\n\n const key = !connected ? 'disconnected' : status;\n const colors = FAB_COLORS[key] ?? FAB_COLORS.idle;\n const isSpinner = status === VocallStatus.Thinking || status === VocallStatus.Executing;\n const isVoiceActive =\n status === VocallStatus.Listening ||\n status === VocallStatus.Recording ||\n status === VocallStatus.Speaking;\n\n return (\n <button\n className={`vocall-fab ${className || ''}`}\n onClick={onClick}\n style={{\n position: 'fixed',\n bottom: 16,\n right: 16,\n width: 52,\n height: 52,\n borderRadius: '50%',\n background: colors.bg,\n color: '#fff',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxShadow: `0 4px 12px ${colors.shadow}`,\n transition: 'all 0.3s ease-in-out',\n zIndex: 1000,\n fontSize: 24,\n ...style,\n }}\n >\n {isSpinner ? (\n <div\n style={{\n width: 24,\n height: 24,\n border: '2px solid rgba(255,255,255,0.3)',\n borderTopColor: '#fff',\n borderRadius: '50%',\n animation: 'vocall-spin 0.8s linear infinite',\n }}\n />\n ) : isVoiceActive ? (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5zm6 6c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z\"/>\n </svg>\n ) : (\n <span>&#129302;</span>\n )}\n </button>\n );\n}\n","// ---------------------------------------------------------------------------\n// VocallStatus - Status indicator pill component\n// ---------------------------------------------------------------------------\n\n'use client';\n\nimport React from 'react';\nimport { useVocall } from '../hooks/use-vocall';\nimport { VocallStatus as VStatus } from '../protocol/types';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface VocallStatusProps {\n /** CSS class */\n className?: string;\n /** Inline styles */\n style?: React.CSSProperties;\n}\n\n// ---------------------------------------------------------------------------\n// Status config\n// ---------------------------------------------------------------------------\n\nconst STATUS_CONFIG: Record<string, { label: string; bg: string; color: string }> = {\n thinking: { label: 'Pensando...', bg: 'rgba(139,92,246,0.12)', color: '#8B5CF6' },\n executing: { label: 'Executando...', bg: 'rgba(245,158,11,0.12)', color: '#F59E0B' },\n speaking: { label: 'Falando...', bg: 'rgba(255,179,71,0.12)', color: '#FFB347' },\n listening: { label: 'Ouvindo...', bg: 'rgba(0,212,255,0.12)', color: '#00D4FF' },\n recording: { label: 'Gravando...', bg: 'rgba(239,68,68,0.12)', color: '#FF4060' },\n};\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function VocallStatusPill({ className, style }: VocallStatusProps) {\n const { status, connected } = useVocall();\n\n const config = STATUS_CONFIG[status];\n if (!config || !connected || status === VStatus.Idle || status === VStatus.Disconnected) {\n return null;\n }\n\n return (\n <div\n className={`vocall-status-pill ${className || ''}`}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n padding: '4px 10px',\n borderRadius: 12,\n fontSize: 11,\n fontWeight: 500,\n background: config.bg,\n color: config.color,\n ...style,\n }}\n >\n <div\n style={{\n width: 12,\n height: 12,\n border: '1.5px solid currentColor',\n borderTopColor: 'transparent',\n borderRadius: '50%',\n animation: 'vocall-spin 0.8s linear infinite',\n }}\n />\n <span>{config.label}</span>\n </div>\n );\n}\n"],"mappings":"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","names":["FieldType","ChatRole","VocallStatus","TARGET_SAMPLE_RATE","useCallback","useEffect","useRef","useRef","useCallback","useEffect","useEffect","useRef","useRef","useEffect","useCallback","useSyncExternalStore","useCallback","useSyncExternalStore","useCallback","useEffect","useRef","useState","jsx","useState","useRef","useEffect","useCallback","jsx","jsx","jsxs"]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@primoia/vocall-react",
3
+ "version": "0.1.0",
4
+ "description": "React/Next.js SDK for Vocall AAP protocol",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "license": "MIT",
22
+ "author": "Primoia <dev@primoia.ai>",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/primoia/vocall-sdk-nextjs.git"
26
+ },
27
+ "homepage": "https://primoia.ai/docs/react",
28
+ "bugs": {
29
+ "url": "https://github.com/primoia/vocall-sdk-nextjs/issues"
30
+ },
31
+ "keywords": [
32
+ "primoia",
33
+ "vocall",
34
+ "react",
35
+ "nextjs",
36
+ "websocket",
37
+ "ui-automation",
38
+ "aap-protocol",
39
+ "ai-agent",
40
+ "hooks"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
44
+ "lint": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:coverage": "vitest run --coverage"
48
+ },
49
+ "peerDependencies": {
50
+ "react": ">=18.0.0",
51
+ "react-dom": ">=18.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "react": "^18.3.0",
55
+ "react-dom": "^18.3.0",
56
+ "@types/react": "^18.3.0",
57
+ "@types/react-dom": "^18.3.0",
58
+ "tsup": "^8.0.0",
59
+ "typescript": "^5.4.0",
60
+ "vitest": "^3.0.0",
61
+ "@vitest/coverage-v8": "^3.0.0",
62
+ "jsdom": "^25.0.0",
63
+ "@testing-library/react": "^16.0.0",
64
+ "@testing-library/jest-dom": "^6.6.0",
65
+ "@testing-library/user-event": "^14.5.0"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public"
69
+ }
70
+ }