npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

presentx

v2.0.0

Published

HTML slide presentation generator with PPTX export, self-healing validation, brand injection, and streaming React viewer

Downloads

90

Readme

presentx

HTML slide presentation generator with PPTX export, self-healing validation, brand injection, and a streaming React viewer.

Generate slide decks from HTML, preview them live with streaming support, and export to editable PowerPoint files -- all client-side.

Features

  • HTML to PPTX -- Convert HTML slides to editable PowerPoint files with native charts, preserved layouts, and editable text
  • Self-healing validator -- Automatically fixes missing dimensions, overflow, flex layout, chart types, and other common LLM output issues
  • Streaming React viewer -- Progressive rendering with skeleton loading, slide counter, and auto-scroll during generation
  • Brand injection -- Automatically inject logos, headers, footers, and copyright into every slide via theme configuration
  • Customizable themes -- Register themes with colors, fonts, chart palettes, table styles, and brand assets
  • LLM prompt generation -- Theme-aware system prompt fragments for teaching LLMs to create slide presentations
  • Native Chart.js support -- Chart.js data in slides is converted to native, editable PowerPoint charts
  • Material Icons -- Automatic rasterization of Material Icons for PPTX compatibility

Installation

npm install presentx

Quick Start

React Viewer

import { SlideViewer } from 'presentx/react';
import 'presentx/styles';

function App() {
  return (
    <SlideViewer
      html={slidesHtml}
      isStreaming={false}
      theme="default"
    />
  );
}

Export to PPTX

import { convertSlidesToPptx, validatePresentation } from 'presentx';

// Validate and self-heal the HTML
const report = validatePresentation(slidesHtml);

// Convert to PowerPoint
const blob = await convertSlidesToPptx(report.fixedHtml, 'My Presentation');

// Download
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'presentation.pptx';
a.click();

LLM Prompt Generation (Node.js)

const { getSlideInstructions } = require('presentx/prompts');

const prompt = getSlideInstructions({ theme: 'my-brand' });
// Returns a system prompt fragment that teaches the LLM how to create slides

Entry Points

| Import | Environment | Contents | |--------|-------------|----------| | presentx | Browser | Converter, validator, themes, brand injection, utilities | | presentx/react | Browser + React | SlideViewer, SlideCounter components | | presentx/prompts | Node.js / Browser | LLM instruction generation, routing rules, theme registry | | presentx/styles | Browser | CSS for the React viewer |


Integrating with LLM / Chat Platforms

presentx is designed to work with any LLM-powered chat platform. The library provides system prompt fragments that teach models how to generate slide HTML, a streaming viewer for real-time preview, and a PPTX converter for download -- all decoupled so you can adopt them independently.

Step 1: Inject the System Prompt (Server-Side)

On your API server, use presentx/prompts to generate a system prompt fragment. This teaches the LLM the slide HTML format, available components (charts, tables, icons, images), layout rules, and your brand palette.

// server/prompts.js
const { getSlideInstructions, registerTheme } = require('presentx/prompts');

// Optional: register a custom brand theme
registerTheme({
  name: 'my-brand',
  colors: {
    primary: '#1a1a2e',
    accent: '#e94560',
    text: '#333333',
    textLight: '#888888',
    bgDark: '#1a1a2e',
    bgLight: '#f5f5f5',
  },
  fonts: { heading: 'Montserrat', body: 'Inter' },
  chartPalette: ['#1a1a2e', '#e94560', '#0f3460', '#16213e', '#533483'],
});

// Generate the slide instructions
const slidePrompt = getSlideInstructions({
  theme: 'my-brand',
  // Optional: add platform-specific instructions on top
  extraInstructions: '## Platform Rules:\n- Use websearch for images\n- No red in charts',
});

// Append to your system prompt
const systemPrompt = baseSystemPrompt + '\n\n' + slidePrompt;

The prompt includes:

  • Slide frame templates (960x540px body slides, title/closing slides)
  • Font scale, layout patterns (KPIs, timelines, grids, charts)
  • Chart.js <canvas> + <script class="chart-data"> syntax
  • Table styling with inline styles for PPTX compatibility
  • Brand palette, fonts, and branding notices from your theme
  • Hard rules (no truncation, complete tags, HTTPS images, etc.)

Step 2: Detect and Render Slides (Client-Side)

When the LLM responds with slide content (using your artifact syntax or a marker), render it with the SlideViewer:

// client/SlideArtifact.tsx
import { SlideViewer } from 'presentx/react';
import { registerTheme } from 'presentx';
import 'presentx/styles';

// Register the same theme on the client (for brand injection in the viewer)
registerTheme({
  name: 'my-brand',
  colors: { /* same as server */ },
  fonts: { /* same as server */ },
  chartPalette: [ /* same as server */ ],
  brand: {
    logo: '<svg>...</svg>',           // Inline SVG or image URL
    logoDark: '<svg>...</svg>',       // Optional: for dark slide backgrounds
    logoIcon: '<svg>...</svg>',       // Optional: small icon for body slide headers
    name: 'My Company',
    header: {
      enabled: true,
      content: 'icon',               // 'logo' | 'icon' | 'name'
      position: 'right',
      height: 36,
    },
    footer: {
      enabled: true,
      showLogo: true,
      logoPosition: 'right',
      copyright: '© 2025 My Company',
      copyrightPosition: 'left',
      height: 32,
    },
  },
});

function SlideArtifact({ html, isStreaming }: { html: string; isStreaming: boolean }) {
  return (
    <SlideViewer
      html={html}
      isStreaming={isStreaming}
      theme="my-brand"
    />
  );
}

The viewer handles:

  • Streaming: progressively renders slides as the LLM generates HTML tokens
  • Validation: auto-fixes malformed slides (missing dimensions, broken tags)
  • Brand injection: overlays logo/header/footer from the theme config
  • Iframe sandbox: slides render in a sandboxed iframe with Tailwind, Google Fonts, Material Icons, and Chart.js pre-loaded

Step 3: PPTX Download (Client-Side)

Add a download button that converts the viewed slides to an editable PowerPoint file:

import { convertSlidesToPptx, validatePresentation, getTheme, injectBrand } from 'presentx';

async function downloadPptx(html: string, title: string) {
  // 1. Validate and self-heal
  const report = validatePresentation(html);

  // 2. Inject brand elements (logo, header, footer)
  const theme = getTheme('my-brand');
  const branded = injectBrand(report.fixedHtml, theme);

  // 3. Convert to PPTX
  const blob = await convertSlidesToPptx(branded, title, { theme: 'my-brand' });

  // 4. Trigger download
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${title}.pptx`;
  a.click();
  URL.revokeObjectURL(url);
}

The PPTX converter:

  • Renders slides in a hidden iframe to capture exact layout via getBoundingClientRect()
  • Converts Chart.js canvases to native editable PPTX charts
  • Rasterizes SVG logos and Material Icons to embedded PNGs
  • Embeds fonts for cross-platform fidelity
  • Preserves table styles, borders, and zebra striping

Architecture Overview

LLM generates HTML slides
    |
    v
getSlideInstructions()     <-- Server: teaches LLM the slide format
    |
    v
validatePresentation()     <-- Client: self-heals malformed output
    |
    v
injectBrand()              <-- Client: adds logo/header/footer from theme
    |
    +---> SlideViewer       <-- Client: live preview (streaming or static)
    |
    +---> convertSlidesToPptx()  <-- Client: export to editable PowerPoint
              |
              v
          PptxGenJS engine  <-- DOM traversal + native charts + font embedding

Themes

Built-in Theme

  • default -- Neutral palette (navy primary, light blue accent, Arial fonts)

Registering a Custom Theme

import { registerTheme } from 'presentx';

registerTheme({
  name: 'my-brand',
  colors: {
    primary: '#1a1a2e',
    accent: '#e94560',
    text: '#333333',
    textLight: '#888888',
    bgDark: '#1a1a2e',
    bgLight: '#f5f5f5',
  },
  fonts: {
    heading: 'Montserrat',
    body: 'Inter',
  },
  chartPalette: ['#1a1a2e', '#e94560', '#0f3460', '#16213e', '#533483'],
  // Optional: additional named colors for the LLM prompt
  extendedColors: {
    borders: '#cccccc',
    'subtle bg': '#e8e8e8',
  },
  // Optional: table styling
  tableStyle: {
    headerBg: '#1a1a2e',
    headerText: '#ffffff',
    headerTextTransform: 'uppercase',
    headerFontWeight: '700',
    evenRowBg: '#ffffff',
    oddRowBg: '#f5f5f5',
    rowText: '#333333',
    rowBorder: true,
    rowBorderColor: 'rgba(0,0,0,0.08)',
  },
});

Brand Injection

Add a brand property to your theme to auto-inject logos, headers, and footers on every slide:

registerTheme({
  name: 'my-brand',
  colors: { /* ... */ },
  fonts: { /* ... */ },
  chartPalette: [ /* ... */ ],
  brand: {
    logo: '<svg>...</svg>',              // Full wordmark (light bg)
    logoDark: '<svg>...</svg>',          // Full wordmark (dark bg)
    logoIcon: '<svg>...</svg>',          // Small icon mark (light bg)
    logoIconDark: '<svg>...</svg>',      // Small icon mark (dark bg)
    name: 'My Company',
    header: {
      enabled: true,                     // Show header bar on body slides
      content: 'icon',                   // 'logo' | 'icon' | 'name'
      position: 'right',                 // 'left' | 'right'
      height: 36,
    },
    footer: {
      enabled: true,                     // Show footer bar
      showLogo: true,
      logoPosition: 'right',
      copyright: '© 2025 My Company',
      copyrightPosition: 'left',
      height: 32,
      showBorder: false,
    },
  },
});

Three slide types get different brand treatment:

  • Title slide (first): Full wordmark logo at top-left + copyright footer
  • Closing slide (last): Same as title slide
  • Body slides (middle): Icon in header bar + full footer with logo and copyright

The LLM prompt automatically includes branding notices so the model knows not to manually add logos.

Slide HTML Format

Slides use Tailwind CSS classes inside 960x540px containers:

<div class="slide w-[960px] h-[540px] overflow-hidden relative flex flex-col pt-[48px] pb-[44px] px-[40px]">
  <div class="shrink-0 pb-2 mb-3 border-b border-black/[0.06]">
    <h2 class="text-[28px] font-bold leading-tight">Slide Title</h2>
  </div>
  <div class="flex-grow min-h-0 flex flex-col">
    <!-- Body content fills remaining space -->
  </div>
</div>

Charts

Chart.js data embedded as JSON inside the slide:

<div class="slide w-[960px] h-[540px] overflow-hidden relative flex flex-col">
  <div class="flex-grow">
    <canvas data-chart-type="bar"></canvas>
  </div>
  <script type="application/json" class="chart-data">
    {"labels":["Q1","Q2","Q3","Q4"],"datasets":[{"label":"Revenue","data":[100,200,300,400]}]}
  </script>
</div>

Charts render as native editable charts in the exported PPTX.

API Reference

Main (presentx)

| Export | Description | |--------|-------------| | convertSlidesToPptx(html, title, options?) | Convert HTML slides to PPTX blob | | validatePresentation(html) | Validate and self-heal slide HTML | | registerTheme(theme) | Register a custom theme | | getTheme(name?) | Get a theme by name (default if omitted) | | listThemes() | List registered theme names | | injectBrand(html, theme) | Inject brand elements into slide HTML | | getBrandCss(theme) | Get brand CSS rules for a theme | | countCompleteSlides(html) | Count completed slides in HTML | | extractCompleteSlides(html) | Extract only complete slide divs | | hasSlideContent(html) | Check if HTML contains slides |

React (presentx/react)

| Export | Description | |--------|-------------| | SlideViewer | Streaming slide viewer component (props: html, isStreaming, theme, className, onValidationReport) | | SlideCounter | Slide count indicator component |

Prompts (presentx/prompts)

| Export | Description | |--------|-------------| | getSlideInstructions(options?) | Generate LLM system prompt for slide creation | | getRoutingRules() | Get artifact routing table entry | | getArtifactTypeDescription() | Get artifact type description string | | registerTheme(theme) | Register a theme (also available from main entry) | | getTheme(name?) | Get a theme (also available from main entry) |

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Author

Varun Muppidi -- GitHub

License

MIT