npm package: @supernal/tts-blog
Description: Automatic TTS integration for blog posts with frontmatter - powered by Supernal TTS
Author: Supernal Intelligence
Latest version: 1.1.1

# @supernal/tts-blog

**Automatic Text-to-Speech for Blog Posts** - Add audio narration to your blog with a single component.

Turn your blog posts into podcasts with just one line of code. Built on [Supernal TTS](https://tts.supernal.ai), this package automatically converts your markdown/MDX blog posts into high-quality audio narration.

## ✨ Features

- 🎙️ **Automatic Integration**: Works with frontmatter-based blogs (Next.js, Gatsby, Docusaurus, etc.)
- 📝 **Smart Content Extraction**: Automatically cleans markdown and extracts readable text
- 🎨 **Zero Config**: Works out of the box with sensible defaults
- ⚙️ **Highly Customizable**: Override voices, providers, and styles via props or frontmatter
- 🚀 **Production Ready**: Built on the proven [@supernal/tts-widget](https://www.npmjs.com/package/@supernal/tts-widget) package
- ♿ **Accessibility**: Makes your content available to visually impaired readers
- 📱 **Mobile Friendly**: Perfect for readers on the go

## 🚀 Quick Start

### Installation

```bash
npm install @supernal/tts-blog @supernal/tts-widget
```

### Basic Usage

```tsx
import { BlogTTSWidget } from '@supernal/tts-blog';
import '@supernal/tts-widget/widget.css';

function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.frontMatter.title}</h1>

      {/* Add TTS with one line */}
      <BlogTTSWidget
        metadata={post.frontMatter}
        content={post.content}
      />

      <div>{post.content}</div>
    </article>
  );
}
```

### Environment Setup

Add your Supernal TTS API key to your environment:

```bash
# .env.local
NEXT_PUBLIC_TTS_API_URL=https://www.tts.supernal.ai
NEXT_PUBLIC_TTS_API_KEY=your_api_key_here
```

Get your free API key at [tts.supernal.ai](https://tts.supernal.ai).

## 📖 Usage

### Frontmatter Configuration

Control TTS behavior directly in your blog post frontmatter:

```yaml
---
title: "My Awesome Blog Post"
author: "Jane Doe"
description: "A brief description of the post"
enableTTS: true          # Enable/disable TTS (default: true)
ttsVoice: "fable"        # Choose voice: fable, nova, onyx, coral, etc.
ttsProvider: "openai"    # Choose provider: openai, cartesia, azure
---

Your blog content here...
```

### Component API

```tsx
interface BlogTTSWidgetProps {
  // Required
  metadata: BlogMetadata;  // Frontmatter from your blog post
  content: string;         // Raw markdown/MDX content

  // Optional
  position?: 'top' | 'bottom';  // Widget position (default: 'top')
  style?: 'default' | 'minimal' | 'outline' | 'card';
  className?: string;
  voice?: string;          // Override frontmatter voice
  provider?: string;       // Override frontmatter provider
  apiUrl?: string;         // Override API URL
  apiKey?: string;         // Override API key
  renderPlaceholder?: (config) => ReactNode;  // Custom rendering
}
```

### Advanced Examples

#### Custom Styling

```tsx
<BlogTTSWidget
  metadata={post.frontMatter}
  content={post.content}
  style="card"
  className="my-custom-class"
/>
```

#### Custom Placeholder Rendering

```tsx
<BlogTTSWidget
  metadata={post.frontMatter}
  content={post.content}
  renderPlaceholder={({ title, author, listeningTime }) => (
    <div className="custom-tts-ui">
      <h3>🎧 Listen to "{title}"</h3>
      {author && <p>Read by AI voice</p>}
      <span>{listeningTime} min audio</span>
    </div>
  )}
/>
```

#### Programmatic Control

```tsx
import { shouldEnableBlogTTS, getBlogTTSConfig } from '@supernal/tts-blog';

// Check if TTS should be enabled
const isEnabled = shouldEnableBlogTTS(post.frontMatter);

// Get TTS configuration
const config = getBlogTTSConfig(post.frontMatter);
// Returns: { enabled: boolean, voice: string, provider: string }
```

## 🎨 Styling

The component uses the styles from `@supernal/tts-widget`. Import the CSS:

```tsx
import '@supernal/tts-widget/widget.css';
```

Or customize with your own styles:

```css
.blog-tts-widget {
  /* Your custom styles */
}
```

## 🌐 Framework Integration

### Next.js 13+ (App Router)

```tsx
// app/blog/[slug]/page.tsx
import { BlogTTSWidget } from '@supernal/tts-blog';
import { getBlogPost } from '@/lib/blog';

export default async function BlogPost({ params }) {
  const post = await getBlogPost(params.slug);

  return (
    <article>
      <h1>{post.frontMatter.title}</h1>
      <BlogTTSWidget
        metadata={post.frontMatter}
        content={post.content}
      />
      <MDXRemote source={post.content} />
    </article>
  );
}
```

### Next.js (Pages Router)

```tsx
// pages/blog/[slug].tsx
import { BlogTTSWidget } from '@supernal/tts-blog';

export default function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.frontMatter.title}</h1>
      <BlogTTSWidget
        metadata={post.frontMatter}
        content={post.content}
      />
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
}
```

### Gatsby

```tsx
// src/templates/blog-post.js
import { BlogTTSWidget } from '@supernal/tts-blog';

export default function BlogPost({ data }) {
  const post = data.markdownRemark;

  return (
    <article>
      <h1>{post.frontmatter.title}</h1>
      <BlogTTSWidget
        metadata={post.frontmatter}
        content={post.rawMarkdownBody}
      />
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
}
```

### Docusaurus

```tsx
// src/theme/BlogPostPage/index.js
import { BlogTTSWidget } from '@supernal/tts-blog';
import OriginalBlogPostPage from '@theme-original/BlogPostPage';

export default function BlogPostPage(props) {
  const { content: BlogPostContent } = props;
  const { frontMatter, metadata } = BlogPostContent;

  return (
    <>
      <BlogTTSWidget
        metadata={frontMatter}
        content={metadata.source}
      />
      <OriginalBlogPostPage {...props} />
    </>
  );
}
```

## 🔧 Utilities

### Clean Markdown Content

```tsx
import { cleanMarkdownContent } from '@supernal/tts-blog';

const plainText = cleanMarkdownContent(markdownContent);
```

### Extract Blog Text with Metadata

```tsx
import { extractBlogText } from '@supernal/tts-blog';

const ttsText = extractBlogText(content, {
  title: 'My Post',
  author: 'John Doe',
  description: 'A great article'
});
// Returns: "My Post. by John Doe. A great article. [content...]"
```

### Estimate Times

```tsx
import { estimateReadingTime, estimateListeningTime } from '@supernal/tts-blog';

const readTime = estimateReadingTime(content); // minutes to read
const listenTime = estimateListeningTime(content); // minutes to listen
```

## 🎙️ Voice Options

### OpenAI Voices
- `alloy` - Neutral, balanced
- `echo` - Warm, expressive
- `fable` - Professional narrator (recommended for blogs)
- `onyx` - Deep, authoritative
- `nova` - Friendly, conversational
- `shimmer` - Clear, energetic
- `coral` - Natural, engaging

### Cartesia Voices
- Ultra-low latency for real-time applications

### Azure Voices
- Cost-effective with extensive language support

## 🛠️ TypeScript Support

Full TypeScript support included:

```tsx
import type { BlogMetadata, BlogTTSWidgetProps, BlogContent } from '@supernal/tts-blog';
```

## 📚 Examples

Check out the [examples directory](./examples) for complete implementations:
- Next.js 13+ App Router
- Next.js Pages Router
- Gatsby
- Docusaurus

## 🤝 Related Packages

- [@supernal/tts-widget](https://www.npmjs.com/package/@supernal/tts-widget) - Core TTS widget
- [@supernal/interface-nextjs](https://www.npmjs.com/package/@supernal/interface-nextjs) - Full interface components

## 📄 License

MIT © Supernal Intelligence

## 🔗 Links

- [Documentation](https://tts.supernal.ai/docs)
- [Live Demo](https://tts.supernal.ai)
- [GitHub](https://github.com/supernalintelligence/supernal-nova)
- [Get API Key](https://tts.supernal.ai)