JSPM

shiki-header-with-copy

1.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • 0
  • Score
    100M100P100Q39755F
  • License MIT

Shiki transformer adding a header with language as title and a copy button

Package Exports

  • shiki-header-with-copy
  • shiki-header-with-copy/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (shiki-header-with-copy) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

shiki-header-with-copy

npm version

A Shiki transformer that adds a header with language display and a copy button to syntax-highlighted code blocks.

Features

  • ⚙️ Customizable header with language name display
  • 📋 One-click copy functionality with visual feedback
  • 🎨 Style it with CSS or Tailwind CSS classes
  • 📦 ESM module support

Demo

Demo of shiki-header-with-copy in action

Open in StackBlitz

Installation

npm install shiki-header-with-copy

Usage

Basic Usage

import { codeToHtml } from 'shiki'
import { addHeaderWithCopy } from 'shiki-header-with-copy'

const code = 'const number = 1'

const html = await codeToHtml(code, {
    lang: 'javascript',
    theme: 'github-dark',
    transformers: [
      addHeaderWithCopy({
        showButtonText: true,
      })
    ],
})

Style it with Tailwind CSS

import { codeToHtml } from 'shiki'
import { addHeaderWithCopy } from 'shiki-header-with-copy'

const html = await codeToHtml(code, {
  theme: 'github-dark',
  transformers: [
    addHeaderWithCopy({
        headerClass: 'flex justify-between items-center px-4 pt-3 text-muted-foreground',
        preClass: 'overflow-x-hidden rounded-lg my-2',
        codeWrapperClass: 'px-4 py-4 overflow-x-auto',
        showButtonText: true,
        copyButtonClass: 'hover:text-accent px-2 group **:data-svg:h-[15px] *:flex *:items-center *:gap-2',
        copyReadyIconClass: '',
        copySuccessIconClass: '**:data-svg:text-green-500',
        langClass: 'text-sm',
    })
  ]
})

Or style with your custom CSS

pre:has(code) {
  border-radius: 10px;
  margin: 15px 0;
  position: relative;
  overflow-x: hidden;
}

pre:has(code) .shiki-header-with-copy {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
}

pre:has(code) .code-wrapper {
  overflow-x: auto;
  padding: 16px;
}

pre:has(code) .shiki-header-with-copy button {
  padding: 0 8px;
  background: none;
  outline: none;
  color: inherit;
  border: none;
}

pre:has(code) .shiki-header-with-copy button:hover {
  color: #3189f0;
}

pre:has(code) .shiki-header-with-copy button span {
  display: flex;
  align-items: center;
  gap: 4px;
}

pre:has(code) .shiki-header-with-copy button svg {
  height: 15px;
}

pre:has(code) .shiki-header-with-copy button span.success {
  color: #009900;
}

Options

Option Type Placed after class Description
headerClass string 'shiki-header-with-copy' CSS class for the header container
langClass string 'lang' CSS class for the language text
copyButtonClass string 'copy' CSS class for the copy button
copyReadyIconClass string 'ready' CSS class for the copy icon
copySuccessIconClass string 'success' CSS class for the success icon
preClass string 'shiki' CSS class for the pre element
codeWrapperClass string 'code-wrapper' CSS class for the code wrapper
showButtonText boolean false Whether to show text labels on the copy button

Generated HTML Structure

The transformer generates the following HTML structure:

<div>
  <!-- ... -->
  <pre class="shiki shiki-themes [...preClass]">
    <div class="shiki-header-with-copy [...headerClass]">
      <span class="lang [...langClass]"><!-- language from fenced code block --></span>
      <button class="copy [...copyButtonClass]" onclick="...">
        <span class="ready [...copyReadyIconClass]">
          <svg><!-- Copy icon SVG --></svg>
          <span>Copy</span> <!-- Shown only if showButtonText is true -->
        </span>
        <span class="success [...copySuccessIconClass]" style="display: none">
          <svg><!-- Checkmark icon SVG --></svg>
          <span>Copied</span> <!-- Shown only if showButtonText is true -->
        </span>
      </button>
    </div>
    <div class="code-wrapper">
        <code><!-- Shiki highlighted code --></code>
    </div>
  </pre>
  <!-- ... -->
</div>

Light/Dark Dual Themes

When using dual themes to prevent shiki from styling the header togather with the rest of the <pre> content, simply add more specific css selector to the dark mode style:

Query-based Dark Mode

@media (prefers-color-scheme: dark) {
  .shiki,
  .shiki code span {  /* <--- "code" added here */
     ...
  }
}

Class-based Dark Mode

html.dark .shiki,
html.dark .shiki code span { /* <--- "code" added here */
  ...
}

Now only your custom classes/styles will be applied without !important overrides from shiki 🎉

API Reference

addHeaderWithCopy(options?: HeaderOptions): ShikiTransformer

Creates a Shiki transformer that adds a header with copy functionality.

HeaderOptions

interface HeaderOptions {
  headerClass?: string
  langClass?: string
  copyButtonClass?: string
  copyReadyIconClass?: string
  copySuccessIconClass?: string
  preClass?: string
  codeWrapperClass?: string
  contentClass?: string
  showButtonText?: boolean
}

Development

# Install dependencies
npm install

# Build the project
npm run build

# Watch for changes
npm run dev

# Clean build artifacts
npm run clean

Contributing

PRs and feature requests welcome!

License

MIT