<p align="center">
  <img alt="Marked Extensions Logo" src="https://raw.githubusercontent.com/fsegurai/marked-extensions/main/public/marked-extensions.svg">
</p>

<p align="center">
  <a href="https://github.com/fsegurai/marked-extensions">
      <img src="https://img.shields.io/azure-devops/build/fsegurai/93779823-473d-4fb3-a5b1-27aaa1a88ea2/18/main?label=Build%20Status&"
          alt="Build Main Status">
  </a>
  <a href="https://www.npmjs.org/package/@fsegurai/marked-extended-embeds">
      <img src="https://img.shields.io/npm/v/@fsegurai/marked-extended-embeds.svg"
          alt="Latest Release">
  </a>
  <br>
  <img alt="GitHub contributors" src="https://img.shields.io/github/contributors/fsegurai/marked-extensions">
  <img alt="Dependency status for repo" src="https://img.shields.io/librariesio/github/fsegurai/marked-extensions">
  <a href="https://opensource.org/licenses/MIT">
    <img alt="GitHub License" src="https://img.shields.io/github/license/fsegurai/marked-extensions">
  </a>
  <br>
  <img alt="Stars" src="https://img.shields.io/github/stars/fsegurai/marked-extensions?style=square&labelColor=343b41"/> 
  <img alt="Forks" src="https://img.shields.io/github/forks/fsegurai/marked-extensions?style=square&labelColor=343b41"/>
</p>

**An extension library for Marked.js to enhance Markdown rendering.**

`@fsegurai/marked-extended-embeds` Marked extension for embedding rich media from various platforms

## 🎯 Overview

The **marked-extended-embeds** extension transforms your Markdown documents into rich, interactive experiences by seamlessly embedding content from 18+ platforms. With automatic platform detection, responsive layouts, privacy controls, and extensive customization options, it's perfect for documentation, blogs, portfolios, and educational content.

### ✨ Key Features

- 🎬 **18+ Supported Platforms**: YouTube, Vimeo, CodePen, Spotify, Twitter, Figma, and more
- 🔍 **Auto-Detection**: Automatically identifies platforms from URLs
- 📐 **Responsive Design**: Automatic aspect ratio handling (16:9, 4:3, 1:1, 21:9, custom)
- 🔒 **Privacy First**: Privacy-enhanced embeds with no-cookie domains
- 🛡️ **Security Built-in**: Iframe sandboxing with configurable permissions
- ⚡ **Performance Optimized**: Lazy loading support for better page speed
- 🎨 **Fully Customizable**: Custom templates, themes, and styling
- ♿ **Accessible**: Semantic HTML with ARIA attributes
- 📱 **Mobile Friendly**: Responsive layouts that work on all devices
- 🎛️ **Provider Configs**: Per-provider customization options
- 🪝 **Callback Hooks**: onLoad and onError event handlers
- 🔧 **Framework Agnostic**: Works with React, Vue, Angular, Svelte, and vanilla JS

### 🎪 Live Demo

Experience all 18+ embed types in action: [View Demo](https://fsegurai.github.io/marked-extensions)

---

### Table of contents

- [Installation](#installation)
- [Usage](#usage)
	- [Basic Usage](#basic-usage)
- [Quick Start](#quick-start)
	- [Syntax & Usage](#syntax--usage)
	- [Supported Platforms](#supported-platforms)
	- [Configuration Options](#configuration-options)
	- [Embed Properties](#embed-properties)
	- [Advanced Examples](#advanced-examples)
	- [Platform-Specific Guides](#platform-specific-guides)
- [More Resources](#more-resources)
	- [Available Extensions](#available-extensions)
	- [Demo Application](#demo-application)
- [License](#license)

## Installation

To add `@fsegurai/marked-extended-embeds` along with Marked.js to your `package.json` use the following commands.

```bash
bun install @fsegurai/marked-extended-embeds marked@>=17.0.0 --save
```

## Usage

### Basic Usage

Import `@fsegurai/marked-extended-embeds` and apply it to your Marked instance as shown below.

## Quick Start

### Basic Syntax

**The extension automatically detects the platform from URLs** or you can explicitly specify the `provider`:

```markdown
::::embed{title="My Video" aspectRatio="16:9"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend
```

```javascript
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

// or UMD script
// <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds/lib/index.umd.js"></script>

marked.use(markedExtendedEmbeds());

### Installation

Install the package using your preferred package manager:

```bash
# Using Bun (recommended)
bun add @fsegurai/marked-extended-embeds

# Using npm
npm install @fsegurai/marked-extended-embeds

# Using yarn
yarn add @fsegurai/marked-extended-embeds

# Using pnpm
pnpm add @fsegurai/marked-extended-embeds
```

### Basic Implementation

```javascript
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

// Import styles (required for functionality)
import '@fsegurai/marked-extended-embeds/styles/embed.css';
// Optional: Import theme for styled appearance
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';

// Register the extension
marked.use(markedExtendedEmbeds());

// Your markdown content with embeds
const markdown = `
# My Document

## Video Tutorial

::::embed{title="Getting Started" aspectRatio="16:9"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

## Code Example

::::embed{provider="codepen" theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend

## Music Playlist

::::embed{title="Coding Playlist"}
https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
::::embedend
`;

// Parse and render
const html = marked.parse(markdown);
console.log(html);
```

### Syntax & Usage

#### Basic Embed Syntax

```markdown
::::embed{properties}
URL or content
::::embedend
```

#### Auto-Detection Examples

The extension automatically detects the platform from the URL:

```markdown
<!-- YouTube video -->
::::embed{title="Tutorial Video"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- Vimeo video -->
::::embed{}
https://vimeo.com/123456789
::::embedend

<!-- CodePen pen -->
::::embed{}
https://codepen.io/username/pen/abc123
::::embedend

<!-- Twitter/X post -->
::::embed{}
https://twitter.com/username/status/1234567890
::::embedend

<!-- Spotify track -->
::::embed{}
https://open.spotify.com/track/abc123
::::embedend

<!-- GitHub Gist -->
::::embed{}
https://gist.github.com/username/abc123def456
::::embedend
```

#### Explicit Provider

You can explicitly specify the provider:

```markdown
::::embed{provider="youtube" title="My Video"}
https://www.youtube.com/watch?v=abc123
::::embedend

::::embed{provider="mermaid" title="System Architecture"}
graph TD;
    A[Client] --> B[Server];
    B --> C[Database];
::::embedend

::::embed{provider="pdf" title="Documentation"}
https://example.com/document.pdf
::::embedend
```

#### Common Property Examples

```markdown
<!-- With aspect ratio -->
::::embed{aspectRatio="16:9"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With custom dimensions -->
::::embed{width="800px" height="600px"}
https://www.vimeo.com/123456789
::::embedend

<!-- With autoplay and muted -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With privacy mode -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With theme -->
::::embed{theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend

<!-- With custom start time -->
::::embed{startTime="30"}
https://www.youtube.com/watch?v=abc123
::::embedend
```
```

The extension automatically detects providers, applies responsive layouts with proper aspect ratios, and includes
accessibility features. All embeds support lazy loading, sandbox security, and customizable styling.

### Importing Styles

This extension **does not automatically inject styles**. You must import the CSS or SCSS files manually to ensure proper styling.

#### Option 1: Using CSS (Recommended for most projects)

```javascript
// Import minimal structural styles (required for functionality)
import '@fsegurai/marked-extended-embeds/styles/embed.css';

// Optional: Import the basic theme
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
```

Or using CDN (vanilla HTML/JavaScript projects):

```html
<!-- Latest version -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@latest/dist/styles/embed.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@latest/dist/styles/embed-theme.css">

<!-- Or lock to a specific version (recommended for production) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@17.0.0/dist/styles/embed.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@17.0.0/dist/styles/embed-theme.css">
```

#### Option 2: Using SCSS (For customization)

```scss
// Import structural styles with customizable variables
@import '@fsegurai/marked-extended-embeds/styles/embed.scss';

// Optional: Import theme with customizable variables
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';

// Customize SCSS variables before importing (optional)
$embed-border-radius: 12px;
$embed-box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';
```

#### Angular Integration

For Angular projects using `ngx-markdown`:

```typescript
// In your angular.json, add the CSS file to styles array:
{
  "styles": [
    "node_modules/@fsegurai/marked-extended-embeds/dist/styles/embed.css",
    "node_modules/@fsegurai/marked-extended-embeds/dist/styles/embed-theme.css",
    // ... other styles
  ]
}

// Or import in your global styles.scss:
@import '@fsegurai/marked-extended-embeds/styles/embed.scss';
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';
```

### Styling Your Embeds

This extension follows a **separation of concerns** approach. The extension generates semantic HTML with CSS classes, while styles are imported separately, giving you full control over the visual appearance.

#### Generated HTML Structure

```html

<div class="marked-extended-embed-container"
     data-provider="youtube"
     data-embed-id="dQw4w9WgXcQ">
    <!-- Loading state -->
    <div class="marked-extended-embed-loading">
        <div class="marked-extended-embed-spinner"></div>
        <p>Loading...</p>
    </div>
  
    <!-- Embedded content (iframe) -->
    <iframe
            src="https://www.youtube.com/embed/dQw4w9WgXcQ"
            loading="lazy"
            sandbox="allow-scripts allow-same-origin"
            allowfullscreen>
    </iframe>

    <!-- Optional caption -->
    <div class="marked-extended-embed-caption">
        Video description
    </div>
</div>
```

#### CSS Classes Reference

| Class | Purpose | Element |
|-------|---------|---------|
| `.marked-extended-embed-container` | Main wrapper | Container |
| `.marked-extended-embed-container[data-provider="youtube"]` | Provider-specific styling | Container |
| `.marked-extended-embed-loading` | Loading state overlay | Loading div |
| `.marked-extended-embed-spinner` | Loading spinner animation | Spinner div |
| `.marked-extended-embed-loading-text` | "Loading..." text | Text element |
| `.marked-extended-embed-caption` | Caption/description | Caption div |
| `iframe` | Embedded content | Iframe |

#### Complete Styling Example

```css
/* Base Container */
.marked-extended-embed-container {
  position: relative;
  width: 100%;
  margin: 1.5rem 0;
  border-radius: 8px;
  overflow: hidden;
  background: #f5f5f5;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* Responsive aspect ratio wrapper */
.marked-extended-embed-container::before {
  content: '';
  display: block;
  padding-top: 56.25%; /* 16:9 aspect ratio */
}

.marked-extended-embed-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
}

/* Loading State */
.marked-extended-embed-loading {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.05);
  z-index: 1;
}

.marked-extended-embed-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e0e0e0;
  border-top-color: #2196F3;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.marked-extended-embed-loading-text {
  margin-top: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: #666;
}

/* Caption Styling */
.marked-extended-embed-caption {
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  color: #666;
  background: #fafafa;
  border-top: 1px solid #e0e0e0;
  text-align: center;
}

/* Provider-Specific Backgrounds */
.marked-extended-embed-container[data-provider="youtube"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="vimeo"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="codepen"] {
  background: #1e1e1e;
}

.marked-extended-embed-container[data-provider="codesandbox"] {
  background: #151515;
}

.marked-extended-embed-container[data-provider="spotify"] {
  background: #191414;
}

.marked-extended-embed-container[data-provider="soundcloud"] {
  background: #ff5500;
}

.marked-extended-embed-container[data-provider="twitter"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="github-gist"] {
  background: #f6f8fa;
}

.marked-extended-embed-container[data-provider="figma"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="mermaid"] {
  background: #fff;
  padding: 1rem;
}

.marked-extended-embed-container[data-provider="pdf"] {
  background: #525659;
}

.marked-extended-embed-container[data-provider="loom"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="miro"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="excalidraw"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="drawio"],
.marked-extended-embed-container[data-provider="diagrams-net"] {
  background: #fff;
}
```

#### Dark Mode Support

```css
/* Light theme */
body.light .marked-extended-embed-container {
  background: #f5f5f5;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

body.light .marked-extended-embed-caption {
  background: #fafafa;
  color: #666;
  border-top-color: #e0e0e0;
}

body.light .marked-extended-embed-loading {
  color: #666;
}

body.light .marked-extended-embed-spinner {
  border-color: #e0e0e0;
  border-top-color: #2196F3;
}

/* Dark theme */
body.dark .marked-extended-embed-container {
  background: #1e1e1e;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

body.dark .marked-extended-embed-caption {
  background: #2a2a2a;
  color: #e0e0e0;
  border-top-color: #3a3a3a;
}

body.dark .marked-extended-embed-loading {
  color: #ccc;
}

body.dark .marked-extended-embed-spinner {
  border-color: #333;
  border-top-color: #2196F3;
}

/* Provider-specific dark mode overrides */
body.dark .marked-extended-embed-container[data-provider="mermaid"],
body.dark .marked-extended-embed-container[data-provider="twitter"],
body.dark .marked-extended-embed-container[data-provider="figma"],
body.dark .marked-extended-embed-container[data-provider="miro"],
body.dark .marked-extended-embed-container[data-provider="excalidraw"],
body.dark .marked-extended-embed-container[data-provider="drawio"],
body.dark .marked-extended-embed-container[data-provider="diagrams-net"] {
  background: #1e1e1e;
}

body.dark .marked-extended-embed-container[data-provider="github-gist"] {
  background: #0d1117;
}
```

#### Custom Aspect Ratios

```css
/* 16:9 (default - videos) */
.marked-extended-embed-container[data-provider="youtube"]::before,
.marked-extended-embed-container[data-provider="vimeo"]::before,
.marked-extended-embed-container[data-provider="loom"]::before {
  padding-top: 56.25%;
}

/* 1:1 (square - music players) */
.marked-extended-embed-container[data-provider="spotify"]::before,
.marked-extended-embed-container[data-provider="soundcloud"]::before {
  padding-top: 100%;
}

/* 4:3 (presentations) */
.marked-extended-embed-container[data-provider="google-slides"]::before {
  padding-top: 75%;
}

/* Full height (code editors) */
.marked-extended-embed-container[data-provider="codepen"]::before,
.marked-extended-embed-container[data-provider="codesandbox"]::before,
.marked-extended-embed-container[data-provider="replit"]::before {
  padding-top: 100%;
  min-height: 500px;
}
```

#### Responsive Adjustments

```css
@media (max-width: 768px) {
  .marked-extended-embed-container {
    margin: 1rem 0;
    border-radius: 4px;
  }

  .marked-extended-embed-caption {
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
  }

  /* Reduce height for code editors on mobile */
  .marked-extended-embed-container[data-provider="codepen"]::before,
  .marked-extended-embed-container[data-provider="codesandbox"]::before {
    min-height: 400px;
  }
}
```

#### Print Styles

```css
@media print {
  .marked-extended-embed-container {
    break-inside: avoid;
    box-shadow: none;
    border: 1px solid #ccc;
  }

  .marked-extended-embed-loading {
    display: none;
  }

  /* Show caption */
  .marked-extended-embed-caption {
    display: block !important;
  }

  /* Hide interactive embeds, show placeholder */
  .marked-extended-embed-container iframe {
    display: none;
  }

  .marked-extended-embed-container::after {
    content: "Embedded content: " attr(data-provider);
    display: block;
    padding: 2rem;
    text-align: center;
    color: #666;
    font-style: italic;
  }
}
```

#### Copy Demo Theme

For complete styling with all 18+ providers: [embed-theme.css](https://github.com/fsegurai/marked-extensions/blob/main/demo/styles/extensions/embed-theme.css)

Check the [demo](https://fsegurai.github.io/marked-extensions) to see all embed types in action.

### Supported Platforms

The extension supports **18 different platforms** with automatic URL detection:

| Platform         | Type          | Example URL                   |
|------------------|---------------|-------------------------------|
| **YouTube**      | Video         | `youtube.com/watch?v=...`     |
| **Vimeo**        | Video         | `vimeo.com/123456789`         |
| **CodePen**      | Code          | `codepen.io/user/pen/abc`     |
| **CodeSandbox**  | Code          | `codesandbox.io/s/abc`        |
| **Twitter**      | Social        | `twitter.com/user/status/123` |
| **GitHub Gist**  | Code          | `gist.github.com/user/abc`    |
| **Spotify**      | Music         | `open.spotify.com/track/abc`  |
| **SoundCloud**   | Music         | `soundcloud.com/user/track`   |
| **SlideShare**   | Presentation  | `slideshare.net/user/slides`  |
| **Figma**        | Design        | `figma.com/file/abc`          |
| **Loom**         | Video         | `loom.com/share/abc`          |
| **Miro**         | Collaboration | `miro.com/app/board/abc`      |
| **Mermaid**      | Diagram       | `mermaid://graph TD; A-->B;`  |
| **Excalidraw**   | Drawing       | `excalidraw.com/#room=abc`    |
| **Draw.io**      | Diagram       | `diagrams.net/...`            |
| **Diagrams.net** | Diagram       | `diagrams.net/...`            |
| **PDF**          | Document      | Any PDF URL                   |
| **iframe**       | Generic       | Any URL                       |

Each platform is automatically detected from the URL pattern. You can also explicitly specify the provider using the
`provider` property.

## Configuration Options

The marked-extended-embeds extension accepts the following configuration options:

- `className`: The base CSS class name for embeds. Defaults to 'marked-extended-embed.'
- `prefixId`: The prefix ID for embed elements. Defaults to 'embed'.
- `defaultAspectRatio`: Default aspect ratio ('16:9', '4:3', '1:1', '21:9'). Defaults to '16:9'.
- `allowFullscreen`: Allow fullscreen mode for embeds. Defaults to true.
- `lazyLoad`: Enable lazy loading for better performance. Defaults to true.
- `privacyMode`: Use privacy-enhanced domains when available. Defaults to false.
- `template`: Custom HTML template for rendering embeds. Defaults to built-in template.
- `customizeToken`: Function to customize embed tokens. Defaults to null.
- `enableSandbox`: Enable iframe sandbox for security. Defaults to true.
  to ['allow-scripts', 'allow-same-origin', 'allow-popups', 'allow-presentation'].
- `providers`: Custom provider configurations. Defaults to {}.
- `onEmbedLoad`: Callback when embed loads. Defaults to null.
- `onEmbedError`: Callback when embed fails. Defaults to null.

**Note:** For styling, import CSS/SCSS files manually (see [Importing Styles](#importing-styles)).

### Embed Properties

Embed syntax supports the following properties:

**Common Properties:**

- `title`: Title/caption for the embed
- `aspectRatio`: Aspect ratio ('16:9', '4:3', '1:1', '21:9', 'custom')
- `width`: Custom width (e.g., '800px')
- `height`: Custom height (e.g., '600px')
- `provider`: Explicit provider name
- `id`: Custom identifier for the embed
- `className`: Custom CSS class

**Media Properties:**

- `autoplay`: Auto-play video/audio (boolean)
- `muted`: Start muted (boolean)
- `loop`: Loop playback (boolean)
- `startTime`: Start time for videos (e.g., '30')
- `controls`: Show media controls (boolean)

**Display Properties:**

- `lazyLoad`: Enable lazy loading (boolean)
- `allowFullscreen`: Allow fullscreen mode (boolean)
- `privacyMode`: Use privacy-enhanced mode (boolean)
- `theme`: Theme setting ('light', 'dark')

**Custom Parameters:**

- `param-*`: Any property starting with 'param-' becomes a URL parameter

### Advanced Examples

#### YouTube Video with Options

```markdown
::::embed{
title="Tutorial Video"
aspectRatio="16:9"
autoplay="true"
muted="true"
startTime="30"
privacyMode="true"
}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend
```

#### CodePen with Dark Theme

```markdown
::::embed{title="Interactive Demo" theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend
```

#### Responsive Design File

```markdown
::::embed{
title="Mobile App Design"
aspectRatio="4:3"
width="100%"
}
https://www.figma.com/file/abc123/Mobile-App
::::embedend
```

#### Spotify Playlist

```markdown
::::embed{title="Coding Playlist" aspectRatio="1:1"}
https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
::::embedend
```

#### CodeSandbox Project

```markdown
::::embed{
title="React Example"
theme="dark"
param-view="preview"
}
https://codesandbox.io/s/react-new
::::embedend
```

#### Mermaid Diagram

```markdown
::::embed{provider="mermaid" title="System Architecture"}
mermaid://graph TD;
A[Client] -->|Request| B[API Gateway];
B --> C[Service 1];
B --> D[Service 2];
C --> E[Database];
D --> E;
::::embedend
```

#### PDF Document

```markdown
::::embed{
provider="pdf"
title="Technical Specification"
aspectRatio="4:3"
}
https://example.com/document.pdf
::::embedend
```

#### Loom Video with Custom Size

```markdown
::::embed{
title="Product Demo"
width="800px"
height="450px"
}
https://www.loom.com/share/abc123
::::embedend
```

#### Twitter Thread

```markdown
::::embed{title="Latest Announcement"}
https://twitter.com/username/status/1234567890
::::embedend
```

#### GitHub Gist

```markdown
::::embed{title="Code Snippet"}
https://gist.github.com/username/abc123
::::embedend
```

#### Excalidraw Sketch

```markdown
::::embed{title="Whiteboard Sketch" aspectRatio="16:9"}
https://excalidraw.com/#room=abc123,def456
::::embedend
```

#### Draw.io Diagram

```markdown
::::embed{provider="drawio" title="System Architecture" aspectRatio="4:3"}
https://viewer.diagrams.net/?highlight=0000ff&edit=_blank&title=diagram
::::embedend
```

## Privacy Mode

Enable privacy-enhanced embedding for supported platforms:

```markdown
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend
```

This uses `youtube-nocookie.com` for YouTube and similar privacy-focused domains when available.

## Custom Provider Configuration

Configure provider-specific settings:

```javascript
marked.use(markedExtendedEmbeds({
    providers: {
        youtube: {
            domain: 'www.youtube-nocookie.com',
            params: {
                rel: '0',
                modestbranding: '1',
            },
        },
        spotify: {
            params: {
                theme: '0',
            },
        },
    },
}));
```

## Security Features

The extension includes several security features:

- **Sandbox by default**: Iframes use sandbox attribute with restricted permissions
- **Configurable permissions**: Customize allowed iframe capabilities
- **Privacy mode**: Use no-cookie domains when available
- **XSS protection**: All user input is escaped
- **HTTPS enforcement**: Embed URLs use secure protocols

## Accessibility

All embeds include proper accessibility features:

- Semantic HTML with proper roles
- Alt text and titles
- Keyboard navigation support
- Screen reader compatibility
- Loading states with aria-labels

### Platform-Specific Guides

#### Video Platforms

**YouTube**
```markdown
<!-- Basic -->
::::embed{}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- With options -->
::::embed{
  autoplay="true"
  muted="true"
  startTime="30"
  privacyMode="true"
  param-rel="0"
  param-modestbranding="1"
}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- YouTube Shorts -->
::::embed{}
https://www.youtube.com/shorts/abc123
::::embedend
```

**Vimeo**
```markdown
<!-- Basic -->
::::embed{}
https://vimeo.com/123456789
::::embedend

<!-- With options -->
::::embed{
  autoplay="true"
  loop="true"
  param-color="ff0000"
  param-portrait="0"
}
https://vimeo.com/123456789
::::embedend
```

**Loom**
```markdown
::::embed{title="Product Demo"}
https://www.loom.com/share/abc123def456
::::embedend
```

#### Code Platforms

**CodePen**
```markdown
<!-- Basic -->
::::embed{}
https://codepen.io/username/pen/abc123
::::embedend

<!-- With theme -->
::::embed{
  theme="dark"
  param-default-tab="js,result"
  param-editable="true"
}
https://codepen.io/username/pen/abc123
::::embedend
```

**CodeSandbox**
```markdown
::::embed{
  theme="dark"
  param-view="preview"
  param-hidenavigation="1"
}
https://codesandbox.io/s/react-new
::::embedend
```

**GitHub Gist**
```markdown
::::embed{title="Code Snippet"}
https://gist.github.com/username/abc123def456
::::embedend
```

#### Music & Audio Platforms

**Spotify**
```markdown
<!-- Track -->
::::embed{aspectRatio="1:1"}
https://open.spotify.com/track/abc123
::::embedend

<!-- Album -->
::::embed{}
https://open.spotify.com/album/abc123
::::embedend

<!-- Playlist -->
::::embed{}
https://open.spotify.com/playlist/abc123
::::embedend

<!-- Podcast -->
::::embed{}
https://open.spotify.com/episode/abc123
::::embedend
```

**SoundCloud**
```markdown
::::embed{param-color="ff5500"}
https://soundcloud.com/artist/track-name
::::embedend
```

#### Social Media

**Twitter/X**
```markdown
::::embed{title="Latest Announcement"}
https://twitter.com/username/status/1234567890
::::embedend

<!-- Also supports X.com URLs -->
::::embed{}
https://x.com/username/status/1234567890
::::embedend
```

#### Design & Collaboration Tools

**Figma**
```markdown
::::embed{
  title="Mobile App Design"
  aspectRatio="4:3"
}
https://www.figma.com/file/abc123/Project-Name
::::embedend

<!-- Prototype view -->
::::embed{}
https://www.figma.com/proto/abc123
::::embedend
```

**Miro**
```markdown
::::embed{title="Brainstorm Board"}
https://miro.com/app/board/abc123
::::embedend
```

**Excalidraw**
```markdown
::::embed{title="Whiteboard Sketch"}
https://excalidraw.com/#room=abc123,def456
::::embedend
```

#### Diagram Tools

**Mermaid**
```markdown
::::embed{provider="mermaid" title="System Flow"}
graph TD;
    A[Start] --> B{Decision};
    B -->|Yes| C[Process 1];
    B -->|No| D[Process 2];
    C --> E[End];
    D --> E;
::::embedend

<!-- Sequence diagram -->
::::embed{provider="mermaid"}
sequenceDiagram
    Alice->>John: Hello John!
    John-->>Alice: Hi Alice!
::::embedend

<!-- Gantt chart -->
::::embed{provider="mermaid"}
gantt
    title Project Timeline
    section Phase 1
    Task 1: 2026-01-01, 30d
    Task 2: 2026-02-01, 20d
::::embedend
```

**Draw.io / Diagrams.net**
```markdown
::::embed{provider="drawio" aspectRatio="4:3"}
https://viewer.diagrams.net/?highlight=0000ff&title=architecture
::::embedend
```

#### Documents

**PDF**
```markdown
::::embed{
  provider="pdf"
  title="Technical Specification"
  aspectRatio="4:3"
}
https://example.com/documents/spec.pdf
::::embedend
```

**SlideShare**
```markdown
::::embed{title="Conference Presentation"}
https://www.slideshare.net/username/presentation-title
::::embedend
```

#### Generic Iframe

**Custom Embed**
```markdown
::::embed{
  provider="iframe"
  title="Custom Content"
  aspectRatio="16:9"
}
https://example.com/custom-embed
::::embedend
```

### Best Practices

#### 1. **Choose Appropriate Aspect Ratios**

Match aspect ratio to content type:

```markdown
<!-- Videos: 16:9 (default) -->
::::embed{aspectRatio="16:9"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Presentations: 4:3 -->
::::embed{aspectRatio="4:3"}
https://www.slideshare.net/user/presentation
::::embedend

<!-- Music players: 1:1 (square) -->
::::embed{aspectRatio="1:1"}
https://open.spotify.com/track/abc123
::::embedend

<!-- Ultra-wide content: 21:9 -->
::::embed{aspectRatio="21:9"}
https://example.com/ultrawide-content
::::embedend
```

#### 2. **Use Privacy Mode for User Privacy**

Enable privacy mode for platforms that support it:

```markdown
<!-- Good: Privacy-conscious -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Uses youtube-nocookie.com instead of youtube.com -->
```

#### 3. **Implement Lazy Loading**

Improve page performance with lazy loading:

```javascript
marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Default is true
}));
```

```markdown
<!-- Lazy loading enabled by default -->
::::embed{}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Explicitly disable for above-the-fold content -->
::::embed{lazyLoad="false"}
https://www.youtube.com/watch?v=abc123
::::embedend
```

#### 4. **Add Meaningful Titles**

Always provide descriptive titles for accessibility:

```markdown
<!-- Good: Descriptive title -->
::::embed{title="React Hooks Tutorial - Complete Guide"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Avoid: Generic title -->
::::embed{title="Video"}
https://www.youtube.com/watch?v=abc123
::::embedend
```

#### 5. **Control Autoplay Wisely**

Use autoplay sparingly and always with muted:

```markdown
<!-- Good: Autoplay with muted -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Avoid: Autoplay without muted (poor UX) -->
::::embed{autoplay="true"}
https://www.youtube.com/watch?v=abc123
::::embedend
```

#### 6. **Optimize for Mobile**

Test embeds on mobile devices:

```css
/* Add mobile-specific styles */
@media (max-width: 768px) {
  .marked-extended-embed-container {
    margin: 1rem 0;
  }
  
  /* Reduce height for code editors on mobile */
  .marked-extended-embed-container[data-provider="codepen"],
  .marked-extended-embed-container[data-provider="codesandbox"] {
    min-height: 400px;
  }
}
```

#### 7. **Use Sandbox for Security**

Keep sandbox enabled (default) for security:

```javascript
// Good: Sandbox enabled (default)
marked.use(markedExtendedEmbeds({
  enableSandbox: true,
  sandboxPermissions: [
    'allow-scripts',
    'allow-same-origin',
    'allow-popups',
    'allow-presentation'
  ]
}));

// Only disable if absolutely necessary
```

### Troubleshooting

#### Embeds Not Displaying

**Problem**: Embeds don't appear on the page.

**Solutions**:

1. **Verify syntax is correct:**
```markdown
<!-- Correct -->
::::embed{}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Wrong -->
:::embed{}  <!-- ❌ Missing colon -->
https://www.youtube.com/watch?v=abc123
:::embedend
```

2. **Check if provider is supported:**
```javascript
// List of supported providers
const supported = [
  'youtube', 'vimeo', 'twitter', 'codepen', 'codesandbox',
  'github-gist', 'spotify', 'soundcloud', 'slideshare',
  'figma', 'loom', 'miro', 'mermaid', 'excalidraw',
  'drawio', 'diagrams-net', 'pdf', 'iframe'
];
```

3. **Verify URL format:**
```markdown
<!-- YouTube - multiple formats supported -->
https://www.youtube.com/watch?v=abc123  ✅
https://youtu.be/abc123                  ✅
https://www.youtube.com/embed/abc123     ✅
https://www.youtube.com/shorts/abc123    ✅
```

4. **Import styles:**
```javascript
import '@fsegurai/marked-extended-embeds/styles/embed.css';
```

#### Styling Not Applied

**Problem**: Embeds appear unstyled or broken.

**Solutions**:

1. **Import CSS files:**
```javascript
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
```

2. **Check CSS file paths in build config**

3. **Verify CSS is loaded (check DevTools Network tab)**

4. **For CDN usage, check for 404 errors**

#### Incorrect Aspect Ratio

**Problem**: Embed appears stretched or squashed.

**Solution**:
```markdown
<!-- Specify correct aspect ratio -->
::::embed{aspectRatio="16:9"}  <!-- Videos -->
https://www.youtube.com/watch?v=abc123
::::embedend

::::embed{aspectRatio="1:1"}  <!-- Music players -->
https://open.spotify.com/track/abc123
::::embedend

::::embed{aspectRatio="4:3"}  <!-- Presentations -->
https://www.slideshare.net/user/slides
::::embedend

<!-- Or use custom dimensions -->
::::embed{width="800px" height="600px"}
https://example.com/content
::::embedend
```

#### Privacy Mode Not Working

**Problem**: Privacy mode doesn't activate.

**Solutions**:

1. **Check if provider supports privacy mode:**
```markdown
<!-- Supported: YouTube, Vimeo -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Not all providers support privacy mode -->
```

2. **Configure globally:**
```javascript
marked.use(markedExtendedEmbeds({
  privacyMode: true  // Enable for all embeds
}));
```

#### Lazy Loading Not Working

**Problem**: All embeds load immediately.

**Solutions**:

1. **Ensure lazy loading is enabled:**
```javascript
marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Default is true
}));
```

2. **Check browser support:**
```html
<!-- Fallback for older browsers -->
<script>
if ('loading' in HTMLIFrameElement.prototype) {
  // Lazy loading supported
} else {
  // Load polyfill or fallback
}
</script>
```

#### Autoplay Not Working

**Problem**: Videos don't autoplay.

**Solutions**:

1. **Always use muted with autoplay:**
```markdown
<!-- Modern browsers require muted for autoplay -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend
```

2. **Check browser autoplay policies:**
- Chrome: Requires muted or user interaction
- Safari: Requires muted
- Firefox: Respects user preferences

#### Sandbox Restrictions

**Problem**: Embed functionality limited due to sandbox.

**Solution**:
```javascript
// Customize sandbox permissions
marked.use(markedExtendedEmbeds({
  enableSandbox: true,
  sandboxPermissions: [
    'allow-scripts',
    'allow-same-origin',
    'allow-popups',
    'allow-presentation',
    'allow-forms',  // Add if forms needed
    'allow-modals'  // Add if modals needed
  ]
}));
```

### Framework Integration

#### React Integration

```tsx
// EmbedMarkdown.tsx
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
import { useEffect, useState, useCallback } from 'react';

// Configure marked
marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Embed loaded: \${provider} (\${id})\`);
  },
  onEmbedError: (id, provider, error) => {
    console.error(\`Embed error: \${provider} (\${id})\`, error);
  }
}));

interface Props {
  content: string;
  enablePrivacy?: boolean;
}

export function EmbedMarkdown({ content, enablePrivacy = true }: Props) {
  const [html, setHtml] = useState('');
  
  useEffect(() => {
    // Reconfigure based on props
    marked.use(markedExtendedEmbeds({
      privacyMode: enablePrivacy,
      lazyLoad: true
    }));
    
    const parsed = marked.parse(content);
    setHtml(parsed);
  }, [content, enablePrivacy]);
  
  return (
    <div 
      className="markdown-with-embeds"
      dangerouslySetInnerHTML={{ __html: html }} 
    />
  );
}

// Usage:
// <EmbedMarkdown content={markdown} enablePrivacy={true} />
```

#### Vue 3 Integration

```vue
<script setup lang="ts">
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
import { computed, watch } from 'vue';

marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Loaded: \${provider}\`);
  }
}));

interface Props {
  content: string;
  privacyMode?: boolean;
  lazyLoad?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  privacyMode: true,
  lazyLoad: true
});

const html = computed(() => {
  marked.use(markedExtendedEmbeds({
    privacyMode: props.privacyMode,
    lazyLoad: props.lazyLoad
  }));
  return marked.parse(props.content);
});

watch([() => props.privacyMode, () => props.lazyLoad], () => {
  console.log('Config changed');
});
</script>

<template>
  <div class="markdown-content" v-html="html" />
</template>
```

#### Angular Integration

```typescript
// embed-markdown.component.ts
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Embed loaded: \${provider}\`);
  }
}));

@Component({
  selector: 'app-embed-markdown',
  template: \`<div [innerHTML]="parsedContent"></div>\`,
  styleUrls: [
    '../node_modules/@fsegurai/marked-extended-embeds/styles/embed.css',
    '../node_modules/@fsegurai/marked-extended-embeds/styles/embed-theme.css'
  ]
})
export class EmbedMarkdownComponent implements OnChanges {
  @Input() content: string = '';
  @Input() privacyMode: boolean = true;
  @Input() lazyLoad: boolean = true;
  parsedContent: SafeHtml = '';

  constructor(private sanitizer: DomSanitizer) {}

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['content'] || changes['privacyMode'] || changes['lazyLoad']) {
      marked.use(markedExtendedEmbeds({
        privacyMode: this.privacyMode,
        lazyLoad: this.lazyLoad
      }));
      
      const html = marked.parse(this.content);
      this.parsedContent = this.sanitizer.bypassSecurityTrustHtml(html);
    }
  }
}
```

#### Svelte Integration

```svelte
<script lang="ts">
  import { marked } from 'marked';
  import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
  import '@fsegurai/marked-extended-embeds/styles/embed.css';
  import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
  
  marked.use(markedExtendedEmbeds({
    lazyLoad: true,
    privacyMode: true,
    onEmbedLoad: (id, provider) => {
      console.log(\`Loaded: \${provider}\`);
    }
  }));
  
  export let content: string;
  export let privacyMode: boolean = true;
  
  $: html = (() => {
    marked.use(markedExtendedEmbeds({
      privacyMode
    }));
    return marked.parse(content);
  })();
</script>

<div class="markdown-content">
  {@html html}
</div>
```

### Performance Optimization

#### 1. **Enable Lazy Loading**
```javascript
marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Defers loading until visible
}));
```

#### 2. **Use Intersection Observer**
```javascript
// Custom intersection observer for advanced control
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const iframe = entry.target.querySelector('iframe');
      if (iframe && iframe.dataset.src) {
        iframe.src = iframe.dataset.src;
        delete iframe.dataset.src;
      }
    }
  });
});

document.querySelectorAll('.marked-extended-embed-container').forEach(embed => {
  observer.observe(embed);
});
```

#### 3. **Optimize for Multiple Embeds**
```markdown
<!-- Load only above-the-fold embeds immediately -->
::::embed{lazyLoad="false"}
https://www.youtube.com/watch?v=first-video
::::embedend

<!-- Lazy load below-the-fold embeds -->
::::embed{lazyLoad="true"}
https://www.youtube.com/watch?v=second-video
::::embedend

::::embed{lazyLoad="true"}
https://www.youtube.com/watch?v=third-video
::::embedend
```

#### 4. **Use Facade Pattern**
```javascript
// Load lightweight preview, full embed on click
marked.use(markedExtendedEmbeds({
  customizeToken: (token) => {
    // Add click-to-load functionality
    token.meta.useFacade = true;
  }
}));
```

### Contributing

Found a bug or have a feature request? Please open an issue on [GitHub](https://github.com/fsegurai/marked-extensions/issues).

### Related Resources

- [Marked.js Documentation](https://marked.js.org/)
- [Extension Demo](https://fsegurai.github.io/marked-extensions)
- [Source Code](https://github.com/fsegurai/marked-extensions)
- [npm Package](https://www.npmjs.com/package/@fsegurai/marked-extended-embeds)
- [Embed Privacy Guide](https://github.com/fsegurai/marked-extensions/wiki/embed-privacy)

### Available Extensions

| Extension   | Package                                                                                                      | Version                                                                    | Description                                                          |
|-------------|--------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------|
| All - Bundle       | [@fsegurai/marked-extended-bundle](https://www.npmjs.com/package/@fsegurai/marked-extended-bundle)             | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-bundle)       | Includes all extensions in a single package for easy integration                  |
| Accordion   | [@fsegurai/marked-extended-accordion](https://www.npmjs.com/package/@fsegurai/marked-extended-accordion)     | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-accordion)   | Add collapsible accordion sections to your markdown                  |
| Alert       | [@fsegurai/marked-extended-alert](https://www.npmjs.com/package/@fsegurai/marked-extended-alert)             | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-alert)       | Create styled alert boxes for important information                  |
| Comments    | [@fsegurai/marked-extended-comments](https://www.npmjs.com/package/@fsegurai/marked-extended-comments)       | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-comments)    | Add comment sections with author and timestamp metadata              |
| Embeds      | [@fsegurai/marked-extended-embeds](https://www.npmjs.com/package/@fsegurai/marked-extended-embeds)           | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-embeds)      | Easily embed content from various platforms (YouTube, Twitter, etc.) |
| Footnote    | [@fsegurai/marked-extended-footnote](https://www.npmjs.com/package/@fsegurai/marked-extended-footnote)       | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-footnote)    | Add footnotes with automatic numbering                               |
| Kanban      | [@fsegurai/marked-extended-kanban](https://www.npmjs.com/package/@fsegurai/marked-extended-kanban)           | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-kanban)      | Create kanban boards with customizable columns and cards             |
| Lists       | [@fsegurai/marked-extended-lists](https://www.npmjs.com/package/@fsegurai/marked-extended-lists)             | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-lists)       | Enhanced list formatting options                                     |
| Slide       | [@fsegurai/marked-extended-slide](https://www.npmjs.com/package/@fsegurai/marked-extended-slide)             | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-slide)       | Create slide decks directly from markdown content                    |
| Spoiler     | [@fsegurai/marked-extended-spoiler](https://www.npmjs.com/package/@fsegurai/marked-extended-spoiler)         | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-spoiler)     | Hide content behind spoiler tags                                     |
| Tables      | [@fsegurai/marked-extended-tables](https://www.npmjs.com/package/@fsegurai/marked-extended-tables)           | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-tables)      | Advanced table formatting with cell spanning                         |
| Tabs        | [@fsegurai/marked-extended-tabs](https://www.npmjs.com/package/@fsegurai/marked-extended-tabs)               | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-tabs)        | Create tabbed content sections                                       |
| Timeline    | [@fsegurai/marked-extended-timeline](https://www.npmjs.com/package/@fsegurai/marked-extended-timeline)       | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-timeline)    | Display content in an interactive timeline format                    |
| Typographic | [@fsegurai/marked-extended-typographic](https://www.npmjs.com/package/@fsegurai/marked-extended-typographic) | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-typographic) | Improve typography with smart quotes, dashes, and more               |

### Demo Application

To see all extensions in action, check out the [[DEMO]](https://fsegurai.github.io/marked-extensions).

To set up the demo locally, follow the next steps:

```shell
git clone https://github.com/fsegurai/marked-extensions.git
bun install
bun start
```

This will serve the application locally at [http://[::1]:8000](http://[::1]:8000).

## License

Licensed under [MIT](https://opensource.org/licenses/MIT).
