Skip to content
Kappa
Code Highlighted
@dicehub/kappav0.4.2

Code Highlighted

Displays syntax-highlighted code with line numbers, line highlights, and a copy affordance.

courant-limit.ts
// Clamp the Courant number before each iteration
const courant = computed(() => Math.min(rawCourant.value, 5));

watch(courant, (value) => {
  solver.setMaxCourant(value);
});
<script setup>
import { CodeHighlighted, ShikiProvider } from "@dicehub/kappa/components/code-highlighted";

const code = `// Clamp the Courant number before each iteration
const courant = computed(() => Math.min(rawCourant.value, 5));

watch(courant, (value) => {
  solver.setMaxCourant(value);
});`;
</script>

<template>
  <ShikiProvider :languages="['typescript']">
    <CodeHighlighted
      title="courant-limit.ts"
      :code="code"
      lang="typescript"
      show-copy-button
    />
  </ShikiProvider>
</template>

Installation

The component lazy-loads shiki, @shikijs/langs, and@shikijs/themes on first render. Install them alongside@dicehub/kappa; they are optional peers, so applications without code display pay nothing.

Import this optional feature only through its dedicated entry point. The root@dicehub/kappa barrel does not export it.

Dedicated Entry Point

import {
  CodeHighlighted,
  CodeHighlightedRoot,
  ShikiProvider,
  useShikiHighlighter,
} from "@dicehub/kappa/components/code-highlighted";

Usage

Wrap code blocks in one ShikiProvider per application or subtree. It creates a single shared highlighter and loads only the listed languages. Until the highlighter is ready, blocks render plain text.

interface BoundaryPatch {
  name: string;
  type: "inlet" | "outlet" | "wall";
  faces: number;
}
<script setup>
import { CodeHighlighted, ShikiProvider } from "@dicehub/kappa/components/code-highlighted";

const code = `interface BoundaryPatch {
  name: string;
  type: "inlet" | "outlet" | "wall";
  faces: number;
}`;
</script>

<template>
  <ShikiProvider :languages="['typescript']">
    <CodeHighlighted :code="code" lang="typescript" />
  </ShikiProvider>
</template>

Composition

Typical part hierarchy
CodeHighlighted.Provider
└── CodeHighlighted.Root

Shiki owns tokenization with TextMate grammars. Kappa owns the provider lifecycle (lazy load, disposal, language sets), the fixed light and dark themes, the display geometry, and semantic tokens. Themes are fixed — github-light andvesper — for consistent rendering in both modes.

  • ShikiProvider loads the engine, themes, and languages once, then shares them.
  • CodeHighlighted renders one block; unknown languages degrade to plain text with a warning.
  • useShikiHighlighter exposes the shared highlighter for custom blocks.

Examples

Title

Add a short file name or label with title. The copy control stays visible in the header.

mesh-partition.ts
function decompose(mesh: Mesh, partitions: number) {
  const regions = mesh.split(partitions);
  const balanced = balance(regions);
  return balanced.map(assignWeights);
}
<CodeHighlighted
  title="mesh-partition.ts"
  :code="code"
  lang="typescript"
  show-copy-button
/>

Languages

Load exactly the languages an application needs; aliases such as ts or sh resolve automatically.

TypeScript
async function fetchRun(id: string): Promise<SimulationRun> {
  const response = await fetch(`/api/runs/${id}`);
  return response.json();
}
Vue
<script setup lang="ts">
const cells = ref(2_400_000);
</script>

<template>
  <span>{{ cells }} cells</span>
</template>
Bash
# Build the library and run the docs
pnpm --filter @dicehub/kappa build
pnpm dev
JSON
{
  "solver": "pimpleFoam",
  "correctors": 6,
  "adaptive": true
}
CSS
.kappa-code-highlighted {
  border: 1px solid var(--kappa-line);
  border-radius: 0.5rem;
}
<ShikiProvider :languages="['typescript', 'vue', 'bash', 'json', 'css']">
  <CodeHighlighted :code="fetchRunSource" lang="typescript" />
  <CodeHighlighted :code="sfcSource" lang="vue" />
  <CodeHighlighted :code="installCommands" lang="bash" />
  <CodeHighlighted :code="solverConfig" lang="json" />
  <CodeHighlighted :code="tokenStyles" lang="css" />
</ShikiProvider>

Highlight Lines

Emphasize 1-indexed lines with highlightLines.

function decompose(mesh: Mesh, partitions: number) {
  const regions = mesh.split(partitions);
  const balanced = balance(regions);
  return balanced.map(assignWeights);
}
<CodeHighlighted :code="code" lang="typescript" :highlight-lines="[3]" />

Custom Highlight Color

Override the --kappa-code-highlight-bg token to recolor highlighted lines.

function decompose(mesh: Mesh, partitions: number) {
  const regions = mesh.split(partitions);
  const balanced = balance(regions);
  return balanced.map(assignWeights);
}
<template>
  <CodeHighlighted
    class="accent-lines"
    :code="code"
    lang="typescript"
    :highlight-lines="[3]"
  />
</template>

<style scoped>
.accent-lines {
  --kappa-code-highlight-bg: color-mix(in oklab, var(--kappa-accent, #4356e8) 14%, transparent);
}
</style>

Line Numbers

Line numbers render for multi-line blocks and stay out of the selection.

import { computed, watch } from "vue";

export function useCourantLimit(raw: Ref<number>) {
  const clamped = computed(() => Math.min(raw.value, 5));

  watch(clamped, (value) => {
    solver.setMaxCourant(value);
  });

  return clamped;
}
<CodeHighlighted :code="code" lang="typescript" show-line-numbers />

Copy Button

showCopyButton adds the copy affordance. Single-line blocks keep the button visible inline instead of floating it on hover.

pnpm add @dicehub/kappa
<CodeHighlighted code="pnpm add @dicehub/kappa" lang="bash" show-copy-button />

Copy Button Labels

The copy button text comes from the provider labels; a block can override them through its own labels prop.

interface BoundaryPatch {
  name: string;
  type: "inlet" | "outlet" | "wall";
  faces: number;
}
<ShikiProvider :languages="['typescript']" :labels="{ copy: 'Kopieren', copied: 'Kopiert!' }">
  <CodeHighlighted :code="code" lang="typescript" show-copy-button />
</ShikiProvider>

Accessibility

  • Code renders as a pre/code region; plain text remains available while the highlighter loads.
  • The optional title renders as a semantic figcaption.
  • Line numbers are decorative and hidden from assistive technology.
  • The Ark UI copy button has an accessible name and a polite status announcement.
  • The button stays visible on touch devices and remains reachable with the keyboard.

API Reference

ShikiProvider

PropTypeDefaultDescription
languagesstring[]requiredLanguages to load; only these get highlighted.
engine"javascript" | "wasm""javascript"Shiki regex engine; JavaScript is smaller, Oniguruma (wasm) is more accurate.
labelsCodeHighlightedLabelsCopy / Copied!Copy-button labels for every CodeHighlighted inside.

CodeHighlighted

PropTypeDefaultDescription
codestringrequiredSource text to display.
langLanguageInput | stringrequiredLanguage id or alias; must be in the provider languages.
titlestring—Optional file name or short label rendered above the code.
showLineNumbersbooleanfalseAdds a line-number column for multi-line code.
highlightLinesnumber[][]1-indexed lines to emphasize.
showCopyButtonbooleanfalseShows the copy button; always visible on single-line blocks.
labelsCodeHighlightedLabelsproviderOverrides provider labels for this block.

Exports

ExportDescription
CodeHighlightedCompound API exposing Root and Provider.
CodeHighlightedRootUnaugmented code display component.
ShikiProviderLazy-loads one shared Shiki highlighter for all child blocks.
useShikiHighlighterAccess highlight, loading state, and labels inside a provider.
normalizeCodeHighlightedLanguageResolves aliases to supported language ids.
DEFAULT_CODE_HIGHLIGHTED_LABELSDefault copy-button labels.
CodeHighlightedLabelsCopy-button label overrides.
CodeHighlightedPropsPublic component props.
LanguageAliasSupported shorthand language ids.
LanguageInputSupported language ids and aliases.
ShikiEngineSupported Shiki engine names.
ShikiProviderPropsProvider props.
SupportedLanguageLanguages available through the dedicated entry point.
UseShikiHighlighterResultReturn type of useShikiHighlighter.