---
title: "हेडलेस हुक"
description: "useThunderPhone React हुकसह पूर्णपणे सानुकूल व्हॉइस UI तयार करा"
---

`useThunderPhone` हुक तुम्हाला वापरकर्ता इंटरफेसवर पूर्ण नियंत्रण देतो, तर ThunderPhone व्हॉइस सत्र, ऑडिओ रूटिंग आणि कनेक्शन स्थिती व्यवस्थापित करते. तुम्हाला पूर्णपणे सानुकूल UI हवे असेल — तुमची स्वतःची बटणे, लेआउट, अॅनिमेशन आणि ब्रँडिंग — आणि ThunderPhone ने पार्श्वभूमीत सर्वकाही हाताळावे असेल, तेव्हा याचा वापर करा.

## हेडलेस हुक कधी वापरावा

पूर्वनिर्मित `ThunderPhoneWidget` घटक बहुतांश वापर प्रकरणे हाताळतो, परंतु तुम्हाला खालील गोष्टी आवश्यक असतील तेव्हा हेडलेस हुक वापरा:

- तुमच्या अॅपच्या डिझाइन सिस्टीमशी जुळणारा पूर्णपणे सानुकूल कॉल UI
- रिअल-टाइम ऑडिओ स्तरांवर आधारित ऑडिओ-प्रतिक्रियाशील व्हिज्युअलायझेशन (वेव्हफॉर्म, ऑर्ब, पल्सिंग इंडिकेटर)
- कॉलपूर्व फॉर्म, कॉलनंतर सर्वेक्षण किंवा व्हॉइससोबत इनलाइन चॅट यांसारखे सानुकूल कॉल फ्लो
- विद्यमान घटक लायब्ररीमध्ये एकत्रीकरण (Material UI, Chakra, Radix इ.)

---

## स्थापना

```bash
npm install @thunderphone/widget
```

<Note>
  हेडलेस हुकसाठी `@thunderphone/widget/style.css` इम्पोर्ट करणे आवश्यक **नाही**, कारण तुम्ही स्वतःचा UI पुरवत आहात. तथापि, तुम्ही तोच `@thunderphone/widget` पॅकेज स्थापित करणे आवश्यक आहे.
</Note>

---

## मूलभूत वापर

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CustomCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  const handleClick = () => {
    if (phone.state === 'connected') {
      phone.disconnect()
    } else {
      phone.connect()
    }
  }

  return (
    <>
      <button onClick={handleClick} disabled={phone.state === 'connecting'}>
        {phone.state === 'connecting'
          ? 'Connecting...'
          : phone.state === 'connected'
            ? 'End call'
            : 'Start call'}
      </button>
      {phone.audio}
    </>
  )
}
```

<Warning>
  **तुम्ही तुमच्या घटक ट्रीमध्ये कुठेतरी `phone.audio` रेंडर करणे आवश्यक आहे.** हा अंतर्निहित ऑडिओ कनेक्शन व्यवस्थापित करणारा अदृश्य React घटक आहे. तुम्ही तो वगळल्यास, कोणताही ऑडिओ प्ले होणार नाही आणि सत्र कार्य करणार नाही.
</Warning>

---

## पर्याय

`UseThunderPhoneOptions` द्वारे हे पर्याय `useThunderPhone` ला द्या:

| पर्याय | प्रकार | आवश्यक | डीफॉल्ट | वर्णन |
|--------|------|----------|---------|-------------|
| `publishableKey` | `string` | होय | -- | प्रकाशित करता येणारी API की (`pk_live_...`). एजंट कीच्या विजेट कॉन्फिगरेशनमधून आपोआप निश्चित केला जातो. |
| `apiBase` | `string` | नाही | `'https://api.thunderphone.com/v1'` | API बेस URL ओव्हरराइड. |
| `language` | `string` | नाही | -- | प्रति-सत्र भाषा ओव्हरराइड — `en`, `es` किंवा `fr-FR` सारखा भाषा कोड किंवा लोकेल. सेट नसल्यास, एजंटची कॉन्फिगर केलेली भाषा लागू होते. |
| `voice` | `string` | नाही | -- | प्रति-सत्र व्हॉइस ओव्हरराइड — `maria` सारखे व्हॉइस नाव. सेट नसल्यास, एजंटचा कॉन्फिगर केलेला व्हॉइस लागू होतो. |
| `context` | `string` | नाही | -- | एजंटला पाठवला जाणारा प्रति-सत्र तथ्यात्मक पृष्ठ किंवा साइट संदर्भ. सर्व्हर-साइड 12,000 वर्णांपर्यंत छाटला जातो. |
| `onConnect` | `() => void` | नाही | -- | व्हॉइस सत्र कनेक्ट झाल्यावर कॉल केले जाते. |
| `onDisconnect` | `() => void` | नाही | -- | सत्र संपल्यावर कॉल केले जाते. |
| `onError` | `(error) => void` | नाही | -- | त्रुटी आल्यावर कॉल केले जाते. त्रुटीमध्ये `error` (कोड) आणि `message` फील्ड असतात. |
| `ringtone` | `boolean \| string` | नाही | `false` | कनेक्ट होत असताना रिंगटोन प्ले करा. डीफॉल्ट रिंगटोनसाठी `true`, किंवा सानुकूल ऑडिओसाठी URL स्ट्रिंग. |

<Note>
  हुक हेडलेस आहे: तो `ThunderPhoneWidget` चे स्वरूप प्रॉप्स (`theme`, `primaryColor`, `title`, `position`, `className`) स्वीकारत **नाही**. ते पास केल्यास TypeScript त्रुटी येते — प्रेझेंटेशन पूर्णपणे तुम्ही तयार करायचे आहे.
</Note>

---

## परतावा मूल्य

हुक `UseThunderPhoneReturn` ऑब्जेक्ट परत करतो:

| गुणधर्म | प्रकार | वर्णन |
|----------|------|-------------|
| `state` | `'idle' \| 'connecting' \| 'connected' \| 'disconnected' \| 'error'` | सध्याची कनेक्शन स्थिती. |
| `connect` | `() => void` | व्हॉइस सेशन सुरू करा. |
| `disconnect` | `() => void` | सध्याचे सेशन समाप्त करा. |
| `toggleMute` | `() => void` | मायक्रोफोन म्यूट चालू/बंद करा. |
| `isMuted` | `boolean` | मायक्रोफोन सध्या म्यूट आहे का. |
| `error` | `string \| undefined` | स्थिती `'error'` असताना त्रुटी संदेश. |
| `agentName` | `string \| undefined` | कनेक्ट केलेल्या एजंटचे प्रदर्शन नाव. |
| `audioLevel` | `number` | **कालबाह्य -- नेहमी `0`.** मागील आवृत्त्यांशी सुसंगततेसाठी ठेवलेला स्थिर प्लेसहोल्डर; तो कधीही अपडेट होत नाही. त्याऐवजी `audioLevelRef.current` वाचा. |
| `audioLevelRef` | `React.RefObject<number>` | रिअल-टाइम ऑडिओ स्तर (0--1) असलेला बदलता ref -- एजंटचा आवाज आणि अभ्यागताच्या मायक्रोफोनपैकी जो जास्त असेल तो -- React च्या render सायकलबाहेर, प्रत्येक अॅनिमेशन फ्रेमवर अपडेट होतो. सुरळीत, अडथळारहित अॅनिमेशनसाठी `requestAnimationFrame` लूपमध्ये `audioLevelRef.current` वाचा किंवा React state मध्ये मूल्य आवश्यक असल्यास ते ठराविक अंतराने नमुना म्हणून घ्या. |
| `audio` | `ReactNode` | ऑडिओ कनेक्शन हाताळणारा अदृश्य घटक -- **render करणे आवश्यक आहे**. |

---

## ऑडिओ-प्रतिक्रियाशील UI

`audioLevelRef` ref तुम्हाला React री-रेंडर ट्रिगर न करता फ्रेम-रेट ऑडिओ स्तर देते, त्यामुळे सुरळीत वेव्हफॉर्म व्हिज्युअलायझेशन, स्पंदनशील ऑर्ब किंवा संभाषणाशी जोडलेले कोणतेही अॅनिमेशन चालवण्यासाठी ते आदर्श आहे. स्तर एजंटचा आवाज किंवा अभ्यागताचा मायक्रोफोन यांपैकी जो जास्त मोठा असेल तो दर्शवतो.

### वेव्हफॉर्म उदाहरण

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function WaveformCall() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')!

    let animId: number
    const draw = () => {
      const level = phone.audioLevelRef.current ?? 0
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      // Draw bars that react to audio level
      const barCount = 24
      const barWidth = canvas.width / barCount
      for (let i = 0; i < barCount; i++) {
        const distance = Math.abs(i - barCount / 2) / (barCount / 2)
        const height = level * canvas.height * (1 - distance * 0.6)
        const y = (canvas.height - height) / 2
        ctx.fillStyle = '#0ea5e9'
        ctx.fillRect(i * barWidth + 1, y, barWidth - 2, height)
      }

      animId = requestAnimationFrame(draw)
    }
    animId = requestAnimationFrame(draw)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <canvas ref={canvasRef} width={240} height={80} />
      )}
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### स्पंदनशील ऑर्ब उदाहरण

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function PulsingOrb() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const orbRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    let animId: number
    const animate = () => {
      const level = phone.audioLevelRef.current ?? 0
      if (orbRef.current) {
        const scale = 1 + level * 0.5
        orbRef.current.style.transform = `scale(${scale})`
        orbRef.current.style.opacity = `${0.6 + level * 0.4}`
      }
      animId = requestAnimationFrame(animate)
    }
    animId = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div style={{ textAlign: 'center' }}>
      <div
        ref={orbRef}
        style={{
          width: 80,
          height: 80,
          borderRadius: '50%',
          background: '#0ea5e9',
          margin: '20px auto',
          transition: 'transform 0.05s ease-out',
        }}
      />
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### बोलण्याचा निर्देशक उदाहरण

व्हॉल्यूमनुसार बदलणाऱ्या React-रेंडर केलेल्या UI साठी -- जसे की थ्रेशोल्ड-आधारित "बोलत आहे" बॅज -- `audioLevelRef.current` मधून ठराविक अंतराने नमुना घ्या आणि निकाल state मध्ये साठवा:

```tsx
import { useEffect, useState } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function SpeakingBadge() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const [speaking, setSpeaking] = useState(false)

  useEffect(() => {
    if (phone.state !== 'connected') {
      setSpeaking(false)
      return
    }
    const interval = setInterval(() => {
      setSpeaking((phone.audioLevelRef.current ?? 0) > 0.1)
    }, 100)
    return () => clearInterval(interval)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <span>{speaking ? 'Speaking' : 'Listening'}</span>
      )}
      <button onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}>
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

<Warning>
  नेहमी `audioLevelRef.current` मधून स्तर वाचा. रिटर्न ऑब्जेक्टवरील `audioLevel` संख्या **अप्रचलित आहे आणि नेहमी `0` असते** -- त्यावर तयार केलेले कोणतेही लॉजिक कोणतीही सूचना न देता शून्य वाचेल.
</Warning>

---

## स्थिती यंत्रणा

`state` प्रॉपर्टी ही जीवनचक्रानुसार बदलते:

```
idle --> connecting --> connected --> disconnected --> idle (after 1.5s)
                  \
                   --> error (stays until connect() is called again)
```

| स्थिती | वर्णन |
|-------|-------------|
| `idle` | कोणतेही सक्रिय सत्र नाही. `connect()` कॉल करण्यासाठी तयार. |
| `connecting` | सत्र स्थापित केले जात आहे. या स्थितीदरम्यान कॉल बटण निष्क्रिय करा. |
| `connected` | व्हॉइस सत्र सक्रिय आहे. वापरकर्ता एजंटशी बोलत आहे. |
| `disconnected` | सत्र व्यवस्थित संपले आहे. 1.5 सेकंदांनंतर ते आपोआप पुन्हा `idle` मध्ये जाते. |
| `error` | काहीतरी चूक झाली. संदेशासाठी `phone.error` तपासा. स्थिती स्वतःहून **रीसेट होत नाही** -- पुन्हा `connect()` कॉल केल्यास नवीन प्रयत्न सुरू होतो आणि त्रुटी रीसेट होते. |

---

## उदाहरणे

### म्यूट नियंत्रणासह

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CallWithMute() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div>
      {phone.state === 'connected' && (
        <div>
          <p>Talking to {phone.agentName ?? 'Agent'}</p>
          <button onClick={phone.toggleMute}>
            {phone.isMuted ? 'Unmute' : 'Mute'}
          </button>
          <button onClick={phone.disconnect}>End call</button>
        </div>
      )}

      {phone.state !== 'connected' && (
        <button
          onClick={phone.connect}
          disabled={phone.state === 'connecting'}
        >
          {phone.state === 'connecting' ? 'Connecting...' : 'Call support'}
        </button>
      )}

      {phone.state === 'error' && (
        <p style={{ color: 'red' }}>{phone.error}</p>
      )}

      {phone.audio}
    </div>
  )
}
```

### रिंगटोनसह

फोन कॉलचे अनुकरण करण्यासाठी कनेक्ट होताना रिंगिंग आवाज वाजवा:

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function PhoneCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connecting'
          ? 'Ringing...'
          : phone.state === 'connected'
            ? 'Hang up'
            : 'Call'}
      </button>
      {phone.audio}
    </>
  )
}
```

रिंगटोन `connecting` स्थितीत लूपमध्ये वाजते आणि एजंट कनेक्ट झाल्यावर हळूहळू बंद होते. अंगभूत डीफॉल्ट रिंगटोनसाठी `true` द्या किंवा तुमची स्वतःची ऑडिओ फाइल वापरण्यासाठी URL स्ट्रिंग द्या.

### इव्हेंट कॉलबॅकसह

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function TrackedCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: () => {
      analytics.track('call_started')
    },
    onDisconnect: () => {
      analytics.track('call_ended')
    },
    onError: (error) => {
      analytics.track('call_error', { code: error.error, message: error.message })
    },
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'Hang up' : 'Talk to AI'}
      </button>
      {phone.audio}
    </>
  )
}
```

### पूर्ण कस्टम UI

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function FullCustomUI() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div className="call-panel">
      <div className="call-status">
        {phone.state === 'idle' && <span>Ready</span>}
        {phone.state === 'connecting' && <span className="pulse">Connecting...</span>}
        {phone.state === 'connected' && (
          <span>On call with {phone.agentName}</span>
        )}
        {phone.state === 'error' && <span className="error">{phone.error}</span>}
      </div>

      <div className="call-controls">
        {phone.state === 'connected' ? (
          <>
            <button className="mute-btn" onClick={phone.toggleMute}>
              {phone.isMuted ? 'Unmute' : 'Mute'}
            </button>
            <button className="end-btn" onClick={phone.disconnect}>
              End
            </button>
          </>
        ) : (
          <button
            className="start-btn"
            onClick={phone.connect}
            disabled={phone.state === 'connecting'}
          >
            Start call
          </button>
        )}
      </div>

      {/* Required -- handles audio under the hood */}
      {phone.audio}
    </div>
  )
}
```

---

## सूचना

<AccordionGroup>
  <Accordion title="नेहमी phone.audio रेंडर करा">
    `phone.audio` घटक अदृश्य आहे, पण आवश्यक आहे. तो तुमच्या JSX मध्ये कुठेही ठेवा -- तो दृश्यमान DOM रेंडर करत नाही, पण अंतर्गतपणे WebRTC ऑडिओ कनेक्शन व्यवस्थापित करतो.
  </Accordion>

  <Accordion title="कनेक्ट करताना बटण निष्क्रिय करा">
    `connecting` स्थिती 1-3 सेकंद टिकू शकते. डुप्लिकेट कनेक्शन प्रयत्न टाळण्यासाठी या स्थितीत कॉल बटण निष्क्रिय करा.
  </Accordion>

  <Accordion title="त्रुटीची स्थिती योग्य प्रकारे हाताळा">
    स्थिती `error` असल्यास, वापरकर्त्याला `phone.error` दाखवा आणि तुमचे कॉल बटण सक्षम ठेवा. हुक स्वतःहून `error` स्थितीतून बाहेर पडत नाही -- पुन्हा `connect()` कॉल केल्याने नवीन प्रयत्न सुरू होतो आणि मागील त्रुटी साफ होते.
  </Accordion>

  <Accordion title="साइड इफेक्ट्ससाठी कॉलबॅक वापरा">
    स्थिती पोल न करता विश्लेषण, लॉगिंग किंवा इतर ॲप्लिकेशन लॉजिक ट्रिगर करण्यासाठी `onConnect`, `onDisconnect`, आणि `onError` कॉलबॅक उपयुक्त आहेत.
  </Accordion>

  <Accordion title="audioLevelRef मधून ऑडिओ स्तर वाचा">
    `audioLevelRef` हा एकमेव लाइव्ह ऑडिओ-स्तर स्रोत आहे. वेव्हफॉर्मसारख्या गुळगुळीत ॲनिमेशनसाठी `requestAnimationFrame` मध्ये `audioLevelRef.current` वाचा (ref वाचल्याने पुन्हा रेंडर होत नाही), किंवा ते अंतराने सॅम्पल करा आणि React-रेंडर केलेल्या UI साठी निकाल स्थितीमध्ये साठवा. `audioLevel` संख्या कालबाह्य आहे आणि नेहमी `0` असते -- तिच्यावर लॉजिक तयार करू नका.
  </Accordion>
</AccordionGroup>
