# 🎙️ Narratify

[![npm version](https://badge.fury.io/js/narratify.svg)](https://badge.fury.io/js/narratify)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)

**Transform your web content into captivating audio experiences** 

Narratify is a powerful React TTS (Text-to-Speech) narration system for Next.js applications. Turn any website into an engaging podcast-like experience with support for multiple voice providers, customizable controls, and seamless integration.

## ✨ Features

- 🎙️ **Multiple Voice Providers**: Support for browser TTS and ElevenLabs API
- 🌍 **Multi-language Support**: Automatic voice selection based on locale
- 🎛️ **Standard Audio Player Controls**: Play, pause, skip, restart, and voice selection
- 📱 **Responsive Design**: Works seamlessly on desktop and mobile devices
- ♿ **Accessibility**: Built with accessibility in mind, keyboard navigation support
- 🎨 **Customizable UI**: Uses Tailwind CSS with fully customizable components
- 🔄 **Continuous Playback**: Automatic progression through content sections
- 🎯 **Visual Highlighting**: Highlights current content during narration
- ⏯️ **Smart Pause/Resume**: Position tracking for seamless pause and resume experience
- ⌨️ **Keyboard Shortcuts**: Space for play/pause, arrow keys for navigation

## 📦 Installation

```bash
# Using pnpm (recommended)
pnpm add narratify

# Using npm
npm install narratify

# Using yarn
yarn add narratify
```

### Prerequisites

- React 18.0.0 or higher
- Next.js 13.0.0 or higher (for full functionality)
- Tailwind CSS (for styling)

### CSS Variables

Narratify uses CSS custom properties for theming and dark mode support. See [CSS_VARIABLES.md](./CSS_VARIABLES.md) for the complete list of required variables and theming examples.

## 🚀 Quick Start

### 1. Basic Usage

```tsx
import { Narratify } from 'narratify';

function MyComponent() {
  const narrationScript = {
    'intro': 'Welcome to Narratify - where websites become podcasts',
    'main-content': 'Transform any content into an engaging audio experience',
    'conclusion': 'Thank you for using Narratify!'
  };

  return (
    <Narratify 
      narrationScript={narrationScript}
      autoStart={false}
      showVoiceSelector={true}
    />
  );
}
```

### 2. With Toggle Button

```tsx
import { NarratifyWithToggle, DEFAULT_VOICE_CONFIGS } from 'narratify';

function MyComponent() {
  const narrationScript = {
    'intro': 'Welcome to our application',
    'main-content': 'This is the main content area'
  };

  return (
    <NarratifyWithToggle 
      narrationScript={narrationScript}
      defaultVoice={DEFAULT_VOICE_CONFIGS["browser-google-us"]}
      continuous={true}
    />
  );
}
```

### 3. Flexible Integration (New in v2.1.0)

For maximum layout flexibility, you can now separate the toggle button and controls:

```tsx
import { 
  NarratifyProvider, 
  NarratifyToggleButtonWithProvider, 
  NarratifyControlsWithProvider 
} from 'narratify';

function MyApp() {
  const narrationScript = {
    'intro': 'Welcome to our flexible integration example',
    'content': 'Toggle button in header, controls at bottom'
  };

  return (
    <NarratifyProvider>
      {/* Header with Toggle Button */}
      <header>
        <h1>My App</h1>
        <NarratifyToggleButtonWithProvider />
      </header>

      {/* Main Content */}
      <main>
        <h2 data-cue="intro">Flexible Integration</h2>
        <p data-cue="content">Content here...</p>
      </main>

      {/* Sticky Controls at Bottom */}
      <NarratifyControlsWithProvider 
        position="bottom"
        sticky={true}
        showVoiceSelector={true}
      />
    </NarratifyProvider>
  );
}
```

### 4. Manual Integration with Context

For complete control, use the context directly:

```tsx
import { 
  NarratifyProvider, 
  useNarratifyContext 
} from 'narratify';

function CustomToggleButton() {
  const { isActive, isPlaying, isPaused, toggleActive } = useNarratifyContext();
  
  return (
    <button onClick={toggleActive}>
      {!isActive ? 'Start' : isPaused ? 'Resume' : 'Pause'}
    </button>
  );
}

function MyApp() {
  return (
    <NarratifyProvider>
      <header>
        <CustomToggleButton />
      </header>
      {/* Your custom controls and content */}
    </NarratifyProvider>
  );
}
```

### 3. Using the Hook Directly

```tsx
import { useNarratify, DEFAULT_VOICE_CONFIGS } from 'narratify';

function MyCustomComponent() {
  const {
    isPlaying,
    currentCue,
    play,
    pause,
    restart
  } = useNarratify({
    narrationScript: myScript,
    voiceConfig: DEFAULT_VOICE_CONFIGS.browser.en
  });

  return (
    <div>
      <button onClick={play}>Play</button>
      <button onClick={pause}>Pause</button>
      <button onClick={restart}>Restart</button>
      {currentCue && <p>Current: {currentCue.text}</p>}
    </div>
  );
}
```

### 4. Content Markup

Mark your content with `data-cue` attributes:

```tsx
<div>
  <h1 data-cue="intro">Welcome to Our App</h1>
  <section data-cue="main-content">
    <p>This content will be highlighted during narration.</p>
  </section>
  <footer data-cue="conclusion">
    <p>Thank you for visiting!</p>
  </footer>
</div>
```

## 📖 Components

### Narratify

The main narrator component with built-in controls.

**Props:**
- `narrationScript?: NarrationScript` - The narration script object
- `autoStart?: boolean` - Whether to start playing automatically
- `startFromCue?: string` - Cue ID to start from
- `showVoiceSelector?: boolean` - Whether to show voice selection
- `defaultVoice?: VoiceConfig` - Default voice configuration
- `continuous?: boolean` - Whether to play continuously
- `locale?: string` - Locale for voice selection
- `onClose?: () => void` - Callback when narrator is closed

### NarratifyWithToggle

A narrator with a toggle button for easy activation.

**Props:**
- Same as Narratify plus:
- `toggleText?: string` - Text for the toggle button
- `toggleIcon?: ReactNode` - Icon for the toggle button

### SimpleNarratify

A simplified version with minimal UI.

### NarratifyControls

Standalone controls component for custom implementations.

### VoiceSelector

Voice selection component with support for multiple providers.

## 🎣 Hooks

### useNarratify

The main hook for TTS functionality.

**Parameters:**
```tsx
interface UseNarratifyProps {
  narrationScript?: NarrationScript;
  voiceConfig?: VoiceConfig;
  autoStart?: boolean;
  continuous?: boolean;
}
```

**Returns:**
```tsx
interface UseNarratifyReturn {
  isPlaying: boolean;
  isPaused: boolean;
  currentCueIndex: number;
  totalCues: number;
  currentCue: NarrationCue | null;
  error: string | null;
  play: () => void;
  pause: () => void;
  restart: () => void;
  playNext: () => void;
  playPrevious: () => void;
}
```

## 🎵 Voice Providers

### Browser TTS

Uses the browser's built-in text-to-speech API.

```tsx
import { DEFAULT_VOICE_CONFIGS } from 'narratify';

const voiceConfig = DEFAULT_VOICE_CONFIGS.browser.en;
// or
const voiceConfig = DEFAULT_VOICE_CONFIGS.browser.es;
const voiceConfig = DEFAULT_VOICE_CONFIGS.browser.fr;
```

### ElevenLabs API

Uses ElevenLabs API for high-quality voices (requires API key).

```tsx
const voiceConfig = {
  provider: 'elevenlabs' as const,
  voiceId: 'your-voice-id',
  apiKey: 'your-api-key'
};
```

For ElevenLabs integration, set up an API route in your Next.js app:

```typescript
// app/api/tts/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { text, voiceId, apiKey } = await request.json();
  
  const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
    method: 'POST',
    headers: {
      'Accept': 'audio/mpeg',
      'Content-Type': 'application/json',
      'xi-api-key': apiKey,
    },
    body: JSON.stringify({
      text,
      model_id: 'eleven_monolingual_v1',
      voice_settings: {
        stability: 0.5,
        similarity_boost: 0.5,
      },
    }),
  });

  if (!response.ok) {
    return NextResponse.json({ error: 'Failed to generate speech' }, { status: 500 });
  }

  const audioBuffer = await response.arrayBuffer();
  const base64Audio = Buffer.from(audioBuffer).toString('base64');
  const audioUrl = `data:audio/mpeg;base64,${base64Audio}`;

  return NextResponse.json({ audioUrl });
}
```

## 📝 Narration Scripts

Narration scripts are objects that map cue IDs to text:

```tsx
const narrationScript = {
  'intro': 'Welcome to our application',
  'main-content': 'This is the main content area',
  'features': 'Here are our amazing features',
  'conclusion': 'Thank you for using our application'
};
```

### Dynamic Script Loading

```tsx
import { loadNarrationScript } from 'narratify';

// Load from JSON file
const script = await loadNarrationScript('/path/to/script.json');

// Load from URL
const script = await loadNarrationScript('https://api.example.com/narration-script');
```

## 🎨 Styling

The package uses Tailwind CSS for styling. You can customize the appearance by:

### 1. CSS Custom Properties

```css
:root {
  --tts-primary-color: #3b82f6;
  --tts-secondary-color: #6b7280;
  --tts-background-color: #ffffff;
  --tts-text-color: #1f2937;
}
```

### 2. Component Class Overrides

```tsx
<Narratify 
  className="custom-narrator-styles"
  narrationScript={script}
/>
```

### 3. Custom UI Components

You can replace the built-in UI components:

```tsx
import { Button, Card } from './custom-ui-components';
```

## ⌨️ Keyboard Shortcuts

- **Space**: Play/Pause
- **Arrow Left**: Previous cue
- **Arrow Right**: Next cue
- **R**: Restart
- **Escape**: Close narrator

## 🔧 Advanced Configuration

### Custom Voice Configuration

```tsx
const customVoice: VoiceConfig = {
  provider: 'browser',
  lang: 'en-US',
  voice: 'Google US English',
  rate: 1.0,
  pitch: 1.0,
  volume: 1.0
};
```

### Event Callbacks

```tsx
<Narratify
  narrationScript={script}
  onPlay={() => console.log('Started playing')}
  onPause={() => console.log('Paused')}
  onEnd={() => console.log('Finished')}
  onCueChange={(cue) => console.log('Current cue:', cue)}
/>
```

## 📱 Mobile Considerations

The package is fully responsive and includes:
- Touch-friendly controls
- Optimized for mobile browsers
- Automatic audio context handling
- Battery-efficient playback

## 🧪 Examples

Check out the `/examples` directory for complete implementation examples:

- **Simple Example**: Basic usage with minimal setup
- **Next.js Example**: Full integration with Next.js app
- **Custom Implementation**: Advanced customization examples

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

### Development Setup

1. Clone the repository:
```bash
git clone https://github.com/ebcont/narratify.git
cd narratify
```

2. Install dependencies:
```bash
pnpm install
```

3. Start development:
```bash
pnpm dev
```

4. Build the package:
```bash
pnpm build
```

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🆘 Support

- 📖 [Documentation](https://github.com/ebcont/narratify#readme)
- 🐛 [Issue Tracker](https://github.com/ebcont/narratify/issues)
- 💬 [Discussions](https://github.com/ebcont/narratify/discussions)

## 🙏 Acknowledgments

- Built with [React](https://reactjs.org/)
- UI components powered by [Radix UI](https://www.radix-ui.com/)
- Styled with [Tailwind CSS](https://tailwindcss.com/)
- Icons by [Lucide React](https://lucide.dev/)

---

**Narratify** - Where websites become podcasts 🎙️  
Made with ❤️ by [EBCONT Studio](https://github.com/ebcont)