react-shiki 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -52
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,8 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
14
14
|
- [Features](#features)
|
|
15
15
|
- [Installation](#installation)
|
|
16
16
|
- [Usage](#usage)
|
|
17
|
-
- [
|
|
17
|
+
- [Common Configuration Options](#common-configuration-options)
|
|
18
|
+
- [Component-specific Props](#component-specific-props)
|
|
18
19
|
- [Integration with react-markdown](#integration-with-react-markdown)
|
|
19
20
|
- [Handling Inline Code](#handling-inline-code)
|
|
20
21
|
- [Multi-theme Support](#multi-theme-support)
|
|
@@ -22,10 +23,10 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
22
23
|
- [Custom Languages](#custom-languages)
|
|
23
24
|
- [Preloading Custom Languages](#preloading-custom-languages)
|
|
24
25
|
- [Custom Transformers](#custom-transformers)
|
|
25
|
-
- [Performance](#performance)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<!--toc:end-->
|
|
26
|
+
- [Performance](#performance)
|
|
27
|
+
- [Throttling Real-time Highlighting](#throttling-real-time-highlighting)
|
|
28
|
+
- [Streaming and LLM Chat UI](#streaming-and-llm-chat-ui)
|
|
29
|
+
<!--toc:end-->
|
|
29
30
|
|
|
30
31
|
## Features
|
|
31
32
|
|
|
@@ -44,19 +45,17 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
44
45
|
## Installation
|
|
45
46
|
|
|
46
47
|
```bash
|
|
47
|
-
npm
|
|
48
|
+
npm i react-shiki
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
## Usage
|
|
51
52
|
|
|
52
|
-
### Basic Usage
|
|
53
|
-
|
|
54
53
|
You can use either the `ShikiHighlighter` component or the `useShikiHighlighter` hook to highlight code.
|
|
55
54
|
|
|
56
55
|
**Using the Component:**
|
|
57
56
|
|
|
58
57
|
```tsx
|
|
59
|
-
import { ShikiHighlighter } from
|
|
58
|
+
import { ShikiHighlighter } from "react-shiki";
|
|
60
59
|
|
|
61
60
|
function CodeBlock() {
|
|
62
61
|
return (
|
|
@@ -67,57 +66,49 @@ function CodeBlock() {
|
|
|
67
66
|
}
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
The `ShikiHighlighter` component accepts the following props:
|
|
71
|
-
|
|
72
|
-
| Prop | Type | Default | Description |
|
|
73
|
-
|------|------|---------|-------------|
|
|
74
|
-
| `language` | `string` | - | Language of the code to highlight |
|
|
75
|
-
| `theme` | `string \| object` | 'github-dark' | Shiki theme to use |
|
|
76
|
-
| `showLanguage` | `boolean` | true | Shows the language name in the top right corner |
|
|
77
|
-
| `addDefaultStyles` | `boolean` | true | Adds default styling to the code block |
|
|
78
|
-
| `as` | `string` | 'pre' | Root element to render |
|
|
79
|
-
| `delay` | `number` | 0 | Delay between highlights in milliseconds |
|
|
80
|
-
| `customLanguages` | `array` | - | Custom languages to preload |
|
|
81
|
-
| `transformers` | `array` | - | Custom Shiki transformers |
|
|
82
|
-
| `className` | `string` | - | Custom class names for the component |
|
|
83
|
-
| `langClassName` | `string` | - | Class names for the language label |
|
|
84
|
-
| `style` | `object` | - | Inline style object for the component |
|
|
85
|
-
| `langStyle` | `object` | - | Inline style object for the language label |
|
|
86
|
-
|
|
87
|
-
|
|
88
69
|
**Using the Hook:**
|
|
89
70
|
|
|
90
71
|
```tsx
|
|
91
|
-
import { useShikiHighlighter } from
|
|
72
|
+
import { useShikiHighlighter } from "react-shiki";
|
|
73
|
+
|
|
74
|
+
function CodeBlock({ code, language }) {
|
|
75
|
+
const highlightedCode = useShikiHighlighter(code, language, "github-dark");
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
const highlightedCode = useShikiHighlighter(code, language, 'github-dark');
|
|
95
|
-
|
|
96
|
-
return <div className="custom-code-block">{highlightedCode}</div>;
|
|
77
|
+
return <div className="code-block">{highlightedCode}</div>;
|
|
97
78
|
}
|
|
98
79
|
```
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
|
103
|
-
|
|
104
|
-
| `code`
|
|
105
|
-
| `language`
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
81
|
+
### Common Configuration Options
|
|
82
|
+
|
|
83
|
+
| Option | Type | Default | Description |
|
|
84
|
+
| ------------------- | ------------------ | --------------- | ------------------------------------------------------------------------- |
|
|
85
|
+
| `code` | `string` | - | Code to highlight |
|
|
86
|
+
| `language` | `string \| object` | - | Language to highlight, built-in or custom textmate grammer object |
|
|
87
|
+
| `theme` | `string \| object` | `'github-dark'` | Single or multi-theme configuration, built-in or custom textmate theme object |
|
|
88
|
+
| `delay` | `number` | `0` | Delay between highlights (in milliseconds) |
|
|
89
|
+
| `transformers` | `array` | `[]` | Custom Shiki transformers for modifying the highlighting output |
|
|
90
|
+
| `customLanguages` | `array` | `[]` | Array of custom languages to preload |
|
|
91
|
+
| `cssVariablePrefix` | `string` | `'--shiki'` | Prefix for CSS variables storing theme colors |
|
|
92
|
+
| `defaultColor` | `string \| false` | `'light'` | Default theme mode when using multiple themes, can also disable default theme |
|
|
93
|
+
|
|
94
|
+
### Component-specific Props
|
|
95
|
+
|
|
96
|
+
The `ShikiHighlighter` component offers minimal built-in styling and customization options out-of-the-box:
|
|
97
|
+
|
|
98
|
+
| Prop | Type | Default | Description |
|
|
99
|
+
| ------------------ | --------- | ------- | ---------------------------------------------------------- |
|
|
100
|
+
| `showLanguage` | `boolean` | `true` | Displays language label in top-right corner |
|
|
101
|
+
| `addDefaultStyles` | `boolean` | `true` | Adds minimal default styling to the highlighted code block |
|
|
102
|
+
| `as` | `string` | `'pre'` | Component's Root HTML element |
|
|
103
|
+
| `className` | `string` | - | Custom class name for the code block |
|
|
104
|
+
| `langClassName` | `string` | - | Class name for styling the language label |
|
|
105
|
+
| `style` | `object` | - | Inline style object for the code block |
|
|
106
|
+
| `langStyle` | `object` | - | Inline style object for the language label |
|
|
117
107
|
|
|
118
108
|
### Integration with react-markdown
|
|
119
109
|
|
|
120
110
|
Create a component to handle syntax highlighting:
|
|
111
|
+
|
|
121
112
|
```tsx
|
|
122
113
|
import ReactMarkdown from "react-markdown";
|
|
123
114
|
import { ShikiHighlighter, isInlineCode } from "react-shiki";
|
|
@@ -141,6 +132,7 @@ const CodeHighlight = ({ className, children, node, ...props }) => {
|
|
|
141
132
|
```
|
|
142
133
|
|
|
143
134
|
Pass the component to react-markdown as a code component:
|
|
135
|
+
|
|
144
136
|
```tsx
|
|
145
137
|
<ReactMarkdown
|
|
146
138
|
components={{
|
|
@@ -251,10 +243,10 @@ Or, when using the hook, pass it to the `theme` parameter:
|
|
|
251
243
|
const highlightedCode = useShikiHighlighter(
|
|
252
244
|
code,
|
|
253
245
|
"tsx",
|
|
254
|
-
{
|
|
246
|
+
{
|
|
255
247
|
light: "github-light",
|
|
256
248
|
dark: "github-dark",
|
|
257
|
-
dim: "github-dark-dimmed"
|
|
249
|
+
dim: "github-dark-dimmed",
|
|
258
250
|
},
|
|
259
251
|
{
|
|
260
252
|
defaultColor: "dark",
|
|
@@ -265,7 +257,7 @@ const highlightedCode = useShikiHighlighter(
|
|
|
265
257
|
See [shiki's documentation](https://shiki.matsu.io/docs/themes) for more information on dual and multi theme support, and for the CSS needed to make the themes reactive to your site's theme.
|
|
266
258
|
|
|
267
259
|
### Custom Themes
|
|
268
|
-
|
|
260
|
+
|
|
269
261
|
Custom themes can be passed as a TextMate theme in JavaScript object. For example, [it should look like this](https://github.com/antfu/textmate-grammars-themes/blob/main/packages/tm-themes/themes/dark-plus.json).
|
|
270
262
|
|
|
271
263
|
```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ type Themes = {
|
|
|
69
69
|
[key: string]: ThemeRegistrationAny | StringLiteralUnion<BundledTheme>;
|
|
70
70
|
};
|
|
71
71
|
/**
|
|
72
|
-
* Configuration options
|
|
72
|
+
* Configuration options specific to react-shiki
|
|
73
73
|
*/
|
|
74
74
|
type ReactShikiOptions = {
|
|
75
75
|
/**
|
|
@@ -82,6 +82,9 @@ type ReactShikiOptions = {
|
|
|
82
82
|
*/
|
|
83
83
|
customLanguages?: LanguageRegistration | LanguageRegistration[];
|
|
84
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* Configuration options for the syntax highlighter
|
|
87
|
+
*/
|
|
85
88
|
type HighlighterOptions = ReactShikiOptions & Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'> & Pick<CodeToHastOptionsCommon, 'transformers'>;
|
|
86
89
|
|
|
87
90
|
/**
|
|
@@ -226,4 +229,4 @@ declare function rehypeInlineCodeProperty(): (tree: any) => undefined;
|
|
|
226
229
|
*/
|
|
227
230
|
declare const isInlineCode: (node: Element) => boolean;
|
|
228
231
|
|
|
229
|
-
export { ShikiHighlighter as default, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
232
|
+
export { type HighlighterOptions, type Language, type Theme, type Themes, ShikiHighlighter as default, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
function v(e,{insertAt:o}={}){if(!e||typeof document>"u")return;let i=document.head||document.getElementsByTagName("head")[0],t=document.createElement("style");t.type="text/css",o==="top"&&i.firstChild?i.insertBefore(t,i.firstChild):i.appendChild(t),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e))}v(`.relative{position:relative}.defaultStyles pre{overflow:auto;border-radius:.5rem;padding:1.25rem 1.5rem}.languageLabel{position:absolute;right:.75rem;top:.5rem;font-family:monospace;font-size:.75rem;letter-spacing:-.05em;color:#6b7280d9}
|
|
2
2
|
`);import A from"react";import{clsx as N}from"clsx";import{useEffect as V,useMemo as O,useRef as _,useState as U}from"react";import q from"html-react-parser";import{createHighlighter as w,createSingletonShorthands as G}from"shiki";import{visit as $}from"unist-util-visit";import{bundledLanguages as z,isSpecialLang as D}from"shiki";function F(){return e=>{$(e,"element",(o,i,t)=>{o.tagName==="code"&&t.tagName!=="pre"&&(o.properties.inline=!0)})}}var B=e=>!(e.children||[]).filter(i=>i.type==="text").map(i=>i.value).join("").includes(`
|
|
3
|
-
`),k={pre(e){return"properties"in e&&(e.properties.tabindex="-1"),e}},E=(e,o,i)=>{let t=Date.now();clearTimeout(o.current.timeoutId);let r=Math.max(0,o.current.nextAllowedTime-t);o.current.timeoutId=setTimeout(()=>{e().catch(console.error),o.current.nextAllowedTime=t+i},r)},f=(e,o=[])=>{if(e&&typeof e=="object")return{isCustom:!0,languageId:e.name,displayLanguageId:e.name,resolvedLanguage:e};if(typeof e=="string"){let i=e;if(e in z||D(e))return{isCustom:!1,languageId:e,displayLanguageId:i};let t=o.find(r=>r.fileTypes?.includes(e)||r.scopeName?.split(".")[1]===e||r.name?.toLowerCase()===e.toLowerCase());return t?{isCustom:!0,languageId:t.name,displayLanguageId:i,resolvedLanguage:t}:{isCustom:!1,languageId:"plaintext",displayLanguageId:i}}return{isCustom:!1,languageId:"plaintext",displayLanguageId:"plaintext"}};function I(e){let o=typeof e=="object"&&"tokenColors"in e&&Array.isArray(e.tokenColors),i=typeof e=="object"&&e!==null&&!o,t=typeof e=="object"&&e!==null&&!o&&Object.entries(e).some(([r,s])=>r&&s&&r.trim()!==""&&s!==""&&(typeof s=="string"||o));return i?{isMultiTheme:!0,themeId:t?`multi-${Object.values(e).map(s=>(typeof s=="string"?s:s?.name)||"custom").sort().join("-")}`:"multi-default",multiTheme:t?e:null,themesToLoad:t?Object.values(e):[]}:{isMultiTheme:!1,themeId:typeof e=="string"?e:e?.name||"custom",singleTheme:e,themesToLoad:[e]}}var M={light:"github-light",dark:"github-dark"},J=G(w),j=new Map,K=async(e,o,i)=>{let t=j.get(e);return t||(t=w({langs:[o],themes:i}),j.set(e,t)),t},R=(e,o,i,t={})=>{let[r,s]=U(null),
|
|
3
|
+
`),k={pre(e){return"properties"in e&&(e.properties.tabindex="-1"),e}},E=(e,o,i)=>{let t=Date.now();clearTimeout(o.current.timeoutId);let r=Math.max(0,o.current.nextAllowedTime-t);o.current.timeoutId=setTimeout(()=>{e().catch(console.error),o.current.nextAllowedTime=t+i},r)},f=(e,o=[])=>{if(e&&typeof e=="object")return{isCustom:!0,languageId:e.name,displayLanguageId:e.name,resolvedLanguage:e};if(typeof e=="string"){let i=e;if(e in z||D(e))return{isCustom:!1,languageId:e,displayLanguageId:i};let t=o.find(r=>r.fileTypes?.includes(e)||r.scopeName?.split(".")[1]===e||r.name?.toLowerCase()===e.toLowerCase());return t?{isCustom:!0,languageId:t.name,displayLanguageId:i,resolvedLanguage:t}:{isCustom:!1,languageId:"plaintext",displayLanguageId:i}}return{isCustom:!1,languageId:"plaintext",displayLanguageId:"plaintext"}};function I(e){let o=typeof e=="object"&&"tokenColors"in e&&Array.isArray(e.tokenColors),i=typeof e=="object"&&e!==null&&!o,t=typeof e=="object"&&e!==null&&!o&&Object.entries(e).some(([r,s])=>r&&s&&r.trim()!==""&&s!==""&&(typeof s=="string"||o));return i?{isMultiTheme:!0,themeId:t?`multi-${Object.values(e).map(s=>(typeof s=="string"?s:s?.name)||"custom").sort().join("-")}`:"multi-default",multiTheme:t?e:null,themesToLoad:t?Object.values(e):[]}:{isMultiTheme:!1,themeId:typeof e=="string"?e:e?.name||"custom",singleTheme:e,themesToLoad:[e]}}var M={light:"github-light",dark:"github-dark"},J=G(w),j=new Map,K=async(e,o,i)=>{let t=j.get(e);return t||(t=w({langs:[o],themes:i}),j.set(e,t)),t},R=(e,o,i,t={})=>{let[r,s]=U(null),m=t.customLanguages?Array.isArray(t.customLanguages)?t.customLanguages:[t.customLanguages]:[],u=m.map(n=>n.name||"").sort().join("-"),h=O(()=>[k,...t.transformers||[]],[t.transformers]),{isMultiTheme:y,themeId:d,multiTheme:T,singleTheme:L,themesToLoad:C}=O(()=>I(i),[i]),{isCustom:a,languageId:g,resolvedLanguage:c}=O(()=>f(o,m),[o,u]),x=`${g}-${d}`,p=_({nextAllowedTime:0,timeoutId:void 0}),S=()=>{let{defaultColor:n,cssVariablePrefix:l}=t,b={lang:g,transformers:h},H=y?{themes:T||M,defaultColor:n,cssVariablePrefix:l}:{theme:L||M.dark};return{...b,...H}};return V(()=>{let n=!0,l=async()=>{let b=a&&c?await K(x,c,C):J,H=S(),P=await b.codeToHtml(e,H);n&&s(q(P))};return t.delay?E(l,p,t.delay):l().catch(console.error),()=>{n=!1,clearTimeout(p.current.timeoutId)}},[e,g,d,u,h,t.delay,t.defaultColor,t.cssVariablePrefix]),r};var Q=({language:e,theme:o,delay:i,transformers:t,defaultColor:r,cssVariablePrefix:s,addDefaultStyles:m=!0,style:u,langStyle:h,className:y,langClassName:d,showLanguage:T=!0,children:L,as:C="pre",customLanguages:a})=>{let g={delay:i,transformers:t,customLanguages:a,defaultColor:r,cssVariablePrefix:s},c=a?Array.isArray(a)?a:[a]:[],{isCustom:x,languageId:p,displayLanguageId:S,resolvedLanguage:n}=f(e,c),l=R(L,e,o,g);return A.createElement(C,{"data-testid":"shiki-container",className:N("relative","not-prose",m&&"defaultStyles",y),style:u,id:"shiki-container"},T&&e?A.createElement("span",{className:N("languageLabel",d),style:h,id:"language-label"},x?`${n?.scopeName.split(".")[1]}`:S||p):null,l)};export{Q as default,B as isInlineCode,F as rehypeInlineCodeProperty,R as useShikiHighlighter};
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["#style-inject:#style-inject","../src/styles.css","../src/ShikiHighlighter.tsx","../src/useShiki.ts","../src/utils.ts"],"sourcesContent":["\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".relative{position:relative}.defaultStyles pre{overflow:auto;border-radius:.5rem;padding:1.25rem 1.5rem}.languageLabel{position:absolute;right:.75rem;top:.5rem;font-family:monospace;font-size:.75rem;letter-spacing:-.05em;color:#6b7280d9}\\n\")","import './styles.css';\n// biome-ignore lint/style/useImportType: React import is needed\nimport React from 'react';\nimport { clsx } from 'clsx';\nimport { useShikiHighlighter } from './useShiki';\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n Themes,\n} from './types';\nimport { resolveLanguage } from './utils';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /**\n * The programming language for syntax highlighting\n * Supports custom textmate grammar objects in addition to Shiki's bundled languages\n * @see https://shiki.style/languages\n */\n language: Language;\n\n /**\n * The code to be highlighted\n */\n children: string;\n\n /**\n * The color theme or themes for syntax highlighting\n * Supports single, dual, or multiple themes\n * Supports custom textmate theme objects in addition to Shiki's bundled themes\n *\n * @example\n * theme='github-dark' // single theme\n * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme\n *\n * @see https://shiki.style/themes\n */\n theme: Theme | Themes;\n\n /**\n * Controls the application of default styles to the generated code blocks\n *\n * Default styles include padding, overflow handling, border radius, language label styling, and font settings\n * @default true\n */\n addDefaultStyles?: boolean;\n\n /**\n * Add custom inline styles to the generated code block\n */\n style?: React.CSSProperties;\n\n /**\n * Add custom inline styles to the language label\n */\n langStyle?: React.CSSProperties;\n\n /**\n * Add custom CSS class names to the generated code block\n */\n className?: string;\n\n /**\n * Add custom CSS class names to the language label\n */\n langClassName?: string;\n\n /**\n * Whether to show the language label\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'pre'\n */\n as?: React.ElementType;\n}\n/**\n * ShikiHighlighter is a React component that provides syntax highlighting for code snippets.\n * It uses Shiki to render beautiful, theme-based syntax highlighting with optimized performance.\n *\n * @example\n * ```tsx\n * <ShikiHighlighter\n * language=\"typescript\"\n * theme=\"github-dark\"\n * delay={100}\n * transformers={[customTransformer]}\n * addDefaultStyles={false}\n * className=\"code-block\"\n * langClassName=\"lang-label\"\n * langStyle={{ color: 'blue' }},\n * style={{ fontSize: '1.2rem' }}\n * >\n * {code}\n * </ShikiHighlighter>\n * ```\n */\nexport const ShikiHighlighter = ({\n language,\n theme,\n delay,\n transformers,\n defaultColor,\n cssVariablePrefix,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'pre',\n customLanguages,\n}: ShikiHighlighterProps): React.ReactElement => {\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n defaultColor,\n cssVariablePrefix,\n };\n\n const normalizedCustomLanguages = customLanguages\n ? Array.isArray(customLanguages)\n ? customLanguages\n : [customLanguages]\n : [];\n\n const { isCustom, languageId, displayLanguageId, resolvedLanguage } =\n resolveLanguage(language, normalizedCustomLanguages);\n\n const highlightedCode = useShikiHighlighter(\n code,\n language,\n theme,\n options\n );\n\n return (\n <Element\n data-testid=\"shiki-container\"\n className={clsx(\n 'relative',\n 'not-prose',\n addDefaultStyles && 'defaultStyles',\n className\n )}\n style={style}\n id=\"shiki-container\"\n >\n {showLanguage && language ? (\n <span\n className={clsx('languageLabel', langClassName)}\n style={langStyle}\n id=\"language-label\"\n >\n {isCustom\n ? `${resolvedLanguage?.scopeName.split('.')[1]}`\n : displayLanguageId || languageId}\n </span>\n ) : null}\n {highlightedCode}\n </Element>\n );\n};\n","import {\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport parse from 'html-react-parser';\n\nimport {\n createHighlighter,\n createSingletonShorthands,\n type Highlighter,\n type CodeToHastOptions,\n type CodeOptionsSingleTheme,\n type CodeOptionsMultipleThemes,\n} from 'shiki';\n\nimport type {\n LanguageRegistration,\n ShikiLanguageRegistration,\n} from './customTypes';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState,\n Themes,\n} from './types';\n\nimport {\n removeTabIndexFromPre,\n throttleHighlighting,\n resolveLanguage,\n resolveTheme,\n} from './utils';\n\nconst DEFAULT_THEMES = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n// Use Shiki managed singleton for bundled languages, create and cache a fresh instance for custom languages\nconst bundledHighlighter = createSingletonShorthands(createHighlighter);\nconst customLangHighlighter = new Map<string, Promise<Highlighter>>();\n\nconst getCachedCustomHighlighter = async (\n cacheKey: string,\n customLang: LanguageRegistration,\n themesToLoad: Theme[]\n) => {\n let instance = customLangHighlighter.get(cacheKey);\n\n if (!instance) {\n instance = createHighlighter({\n langs: [customLang as ShikiLanguageRegistration],\n themes: themesToLoad,\n });\n customLangHighlighter.set(cacheKey, instance);\n }\n return instance;\n};\n\n/**\n * A React hook that provides syntax highlighting using Shiki.\n * Supports single theme and multi-theme highlighting, custom themes\n * and languages, custom transformers, and optional throttling.\n *\n * ```ts\n * // Basic Usage\n * const highlightedCode = useShikiHighlighter( code, 'typescript', 'github-dark');\n * ```\n *\n * ```ts\n * // Custom Languages, Transformers, and Delay\n * const highlightedCode = useShikiHighlighter(code, language, theme, {\n * transformers: [customTransformer],\n * delay: 150\n * customLanguages: ['bosque', 'mcfunction']\n * });\n * ```\n *\n * ```ts\n * // Multiple Themes, Custom Languages, Delay, and Custom Transformers\n * const highlightedCode = useShikiHighlighter(\n * code,\n * language,\n * {\n * light: 'github-light',\n * dark: 'github-dark',\n * dim: 'github-dark-dimmed'\n * },\n * {\n * delay: 150,\n * defaultColor: 'dim',\n * transformers: [customTransformer],\n * customLanguages: ['bosque', 'mcfunction']\n * }\n * );\n * ```\n */\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptions = {}\n) => {\n const [highlightedCode, setHighlightedCode] =\n useState<ReactNode | null>(null);\n\n const normalizedCustomLanguages: LanguageRegistration[] =\n options.customLanguages\n ? Array.isArray(options.customLanguages)\n ? options.customLanguages\n : [options.customLanguages]\n : [];\n\n const customLangId = normalizedCustomLanguages\n .map((lang) => lang.name || '')\n .sort()\n .join('-');\n\n const transformers = useMemo(() => {\n return [removeTabIndexFromPre, ...(options.transformers || [])];\n }, [options.transformers]);\n\n const { isMultiTheme, themeId, multiTheme, singleTheme, themesToLoad } =\n useMemo(() => resolveTheme(themeInput), [themeInput]);\n\n const { isCustom, languageId, resolvedLanguage } = useMemo(\n () => resolveLanguage(lang, normalizedCustomLanguages),\n [lang, customLangId]\n );\n\n const cacheKey = `${languageId}-${themeId}`;\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n const buildShikiOptions = (): CodeToHastOptions => {\n const { defaultColor, cssVariablePrefix } = options;\n const commonOptions = { lang: languageId, transformers };\n\n const themeOptions = isMultiTheme\n ? ({\n themes: multiTheme || DEFAULT_THEMES,\n defaultColor,\n cssVariablePrefix,\n } as CodeOptionsMultipleThemes)\n : ({\n theme: singleTheme || DEFAULT_THEMES.dark,\n } as CodeOptionsSingleTheme);\n\n return { ...commonOptions, ...themeOptions };\n };\n\n useEffect(() => {\n let isMounted = true;\n\n const highlightCode = async () => {\n const codeHighlighter =\n isCustom && resolvedLanguage\n ? await getCachedCustomHighlighter(\n cacheKey,\n resolvedLanguage,\n themesToLoad\n )\n : bundledHighlighter;\n\n const highlighterOptions: CodeToHastOptions = buildShikiOptions();\n\n const html = await codeHighlighter.codeToHtml(\n code,\n highlighterOptions\n );\n if (isMounted) {\n setHighlightedCode(parse(html));\n }\n };\n\n if (options.delay) {\n throttleHighlighting(highlightCode, timeoutControl, options.delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [\n code,\n languageId,\n themeId,\n customLangId,\n transformers,\n options.delay,\n options.defaultColor,\n options.cssVariablePrefix,\n ]);\n\n return highlightedCode;\n};\n","import { visit } from 'unist-util-visit';\nimport { bundledLanguages, isSpecialLang } from 'shiki';\nimport type { ShikiTransformer, ThemeRegistrationAny } from 'shiki';\nimport type { Element } from 'hast';\nimport type { Language, Theme, Themes, TimeoutState } from './types';\nimport type { LanguageRegistration } from './customTypes';\n\n/**\n * Type for the HAST node, use to type `node` from react-markdown\n */\nexport type { Element };\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements\n * Sets 'inline' property to true if the <code> is not within a <pre> tag\n *\n * Pass this plugin to the `rehypePlugins` prop of react-markdown\n * You can then access `inline` as a prop in components passed to react-markdown\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return (tree: any): undefined => {\n visit(tree, 'element', (node: Element, _index, parent: Element) => {\n if (node.tagName === 'code' && parent.tagName !== 'pre') {\n node.properties.inline = true;\n }\n });\n };\n}\n\n/**\n * Function to determine if code is inline based on the presence of line breaks\n *\n * @example\n * const isInline = node && isInlineCode(node: Element)\n */\nexport const isInlineCode = (node: Element): boolean => {\n const textContent = (node.children || [])\n .filter((child) => child.type === 'text')\n .map((child) => child.value)\n .join('');\n\n return !textContent.includes('\\n');\n};\n\n/**\n * Shiki transformer to remove tabindex from <pre> elements\n *\n * Consider retaining tabindex for WCAG 3.1 compliance, scrollable code blocks should be focusable\n * https://github.com/shikijs/shiki/issues/428\n * https://www.w3.org/WAI/standards-guidelines/act/rules/0ssw9k/proposed/\n */\nexport const removeTabIndexFromPre: ShikiTransformer = {\n pre(node) {\n if ('properties' in node) {\n node.properties.tabindex = '-1';\n }\n return node;\n },\n};\n\n/**\n * Optionally throttles rapid sequential highlighting operations\n * Exported for testing in __tests__/throttleHighlighting.test.ts\n *\n * @example\n * const timeoutControl = useRef<TimeoutState>({\n * nextAllowedTime: 0,\n * timeoutId: undefined\n * });\n *\n * throttleHighlighting(highlightCode, timeoutControl, 1000);\n */\nexport const throttleHighlighting = (\n performHighlight: () => Promise<void>,\n timeoutControl: React.MutableRefObject<TimeoutState>,\n throttleMs: number\n) => {\n const now = Date.now();\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(0, timeoutControl.current.nextAllowedTime - now);\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight().catch(console.error);\n timeoutControl.current.nextAllowedTime = now + throttleMs;\n }, delay);\n};\n\ntype ResolvedLanguage = {\n isCustom: boolean;\n languageId: string;\n displayLanguageId?: string;\n resolvedLanguage?: LanguageRegistration;\n};\n\n/**\n * Determines whether a language is custom or built-in, normalizes\n * the language identifier and returns the resolved language with metadata\n *\n * @param lang - The language identifier, either as a string name or language object\n * @param customLanguages - Optional array of custom language definitions\n * @returns Object containing:\n * - isCustom: Whether the language requires custom highlighting\n * - languageId: The normalized language identifier\n * - displayLanguageId: The display name for language label\n * - resolvedLanguage: The full language definition if custom\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages: LanguageRegistration[] = []\n): ResolvedLanguage => {\n // Language is custom but not preloaded\n if (lang && typeof lang === 'object') {\n return {\n isCustom: true,\n languageId: lang.name,\n displayLanguageId: lang.name,\n resolvedLanguage: lang,\n };\n }\n\n // Language is a string\n if (typeof lang === 'string') {\n const displayLanguageId = lang;\n\n // Check if its built-in\n if (lang in bundledLanguages || isSpecialLang(lang)) {\n return {\n isCustom: false,\n languageId: lang,\n displayLanguageId,\n };\n }\n\n // Check if it matches a preloaded custom language\n const customMatch = customLanguages.find(\n (cl) =>\n cl.fileTypes?.includes(lang) ||\n cl.scopeName?.split('.')[1] === lang ||\n cl.name?.toLowerCase() === lang.toLowerCase()\n );\n\n if (customMatch) {\n return {\n isCustom: true,\n languageId: customMatch.name,\n displayLanguageId,\n resolvedLanguage: customMatch,\n };\n }\n\n // If unknown, highlight in plaintext but display unknown language\n return {\n isCustom: false,\n languageId: 'plaintext',\n displayLanguageId,\n };\n }\n\n // Fallback\n return {\n isCustom: false,\n languageId: 'plaintext',\n displayLanguageId: 'plaintext',\n };\n};\n\n/**\n * Determines theme configuration and returns the resolved theme with metadata\n * @param themeInput - The theme input, either as a string name or theme object\n * @returns Object containing:\n * - isMultiTheme: If theme input is a multi-theme configuration\n * - themeId: Theme reference identifier\n * - multiTheme: The multi-theme config if it exists\n * - singleTheme: The single theme if it exists\n * - themesToLoad: The themes to load when creating the highlighter\n */\nexport function resolveTheme(themeInput: Theme | Themes): {\n isMultiTheme: boolean;\n themeId: Theme;\n multiTheme?: Themes | ThemeRegistrationAny | null;\n singleTheme?: Theme | undefined;\n themesToLoad: Theme[];\n} {\n const isTextmateTheme =\n typeof themeInput === 'object' &&\n 'tokenColors' in themeInput &&\n Array.isArray(themeInput.tokenColors);\n\n // Assume non textmate objects are multi theme configs\n const isMultiThemeConfig =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme;\n\n const validMultiThemeObj =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme &&\n Object.entries(themeInput).some(\n ([key, value]) =>\n key &&\n value &&\n key.trim() !== '' &&\n value !== '' &&\n (typeof value === 'string' || isTextmateTheme)\n );\n\n if (isMultiThemeConfig) {\n const themeId = validMultiThemeObj\n ? `multi-${Object.values(themeInput)\n .map(\n (theme) =>\n (typeof theme === 'string' ? theme : theme?.name) ||\n 'custom'\n )\n .sort()\n .join('-')}`\n : 'multi-default';\n\n // If config is invalid, return null to handle fallback in `buildShikiOptions()`\n return {\n isMultiTheme: true,\n themeId,\n multiTheme: validMultiThemeObj ? themeInput : null,\n themesToLoad: validMultiThemeObj ? Object.values(themeInput) : [],\n };\n }\n\n return {\n isMultiTheme: false,\n themeId:\n typeof themeInput === 'string'\n ? themeInput\n : themeInput?.name || 'custom',\n singleTheme: themeInput,\n themesToLoad: [themeInput],\n };\n}\n"],"mappings":"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","names":["styleInject","css","insertAt","head","style","styleInject","React","clsx","useEffect","useMemo","useRef","useState","parse","createHighlighter","createSingletonShorthands","visit","bundledLanguages","isSpecialLang","rehypeInlineCodeProperty","tree","node","_index","parent","isInlineCode","child","removeTabIndexFromPre","throttleHighlighting","performHighlight","timeoutControl","throttleMs","now","delay","resolveLanguage","lang","customLanguages","displayLanguageId","customMatch","cl","resolveTheme","themeInput","isTextmateTheme","isMultiThemeConfig","validMultiThemeObj","key","value","theme","DEFAULT_THEMES","bundledHighlighter","createSingletonShorthands","createHighlighter","customLangHighlighter","getCachedCustomHighlighter","cacheKey","customLang","themesToLoad","instance","useShikiHighlighter","code","lang","themeInput","options","highlightedCode","setHighlightedCode","useState","normalizedCustomLanguages","customLangId","transformers","useMemo","removeTabIndexFromPre","isMultiTheme","themeId","multiTheme","singleTheme","resolveTheme","isCustom","languageId","resolvedLanguage","resolveLanguage","timeoutControl","useRef","buildShikiOptions","defaultColor","cssVariablePrefix","commonOptions","themeOptions","useEffect","isMounted","highlightCode","codeHighlighter","highlighterOptions","html","parse","throttleHighlighting","ShikiHighlighter","language","theme","delay","transformers","defaultColor","cssVariablePrefix","addDefaultStyles","style","langStyle","className","langClassName","showLanguage","code","Element","customLanguages","options","normalizedCustomLanguages","isCustom","languageId","displayLanguageId","resolvedLanguage","resolveLanguage","highlightedCode","useShikiHighlighter","React","clsx"]}
|
|
1
|
+
{"version":3,"sources":["#style-inject:#style-inject","../src/styles.css","../src/component.tsx","../src/hook.ts","../src/utils.ts"],"sourcesContent":["\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".relative{position:relative}.defaultStyles pre{overflow:auto;border-radius:.5rem;padding:1.25rem 1.5rem}.languageLabel{position:absolute;right:.75rem;top:.5rem;font-family:monospace;font-size:.75rem;letter-spacing:-.05em;color:#6b7280d9}\\n\")","import './styles.css';\n// biome-ignore lint/style/useImportType: React import is needed\nimport React from 'react';\nimport { clsx } from 'clsx';\nimport { useShikiHighlighter } from './hook';\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n Themes,\n} from './types';\nimport { resolveLanguage } from './utils';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /**\n * The programming language for syntax highlighting\n * Supports custom textmate grammar objects in addition to Shiki's bundled languages\n * @see https://shiki.style/languages\n */\n language: Language;\n\n /**\n * The code to be highlighted\n */\n children: string;\n\n /**\n * The color theme or themes for syntax highlighting\n * Supports single, dual, or multiple themes\n * Supports custom textmate theme objects in addition to Shiki's bundled themes\n *\n * @example\n * theme='github-dark' // single theme\n * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme\n *\n * @see https://shiki.style/themes\n */\n theme: Theme | Themes;\n\n /**\n * Controls the application of default styles to the generated code blocks\n *\n * Default styles include padding, overflow handling, border radius, language label styling, and font settings\n * @default true\n */\n addDefaultStyles?: boolean;\n\n /**\n * Add custom inline styles to the generated code block\n */\n style?: React.CSSProperties;\n\n /**\n * Add custom inline styles to the language label\n */\n langStyle?: React.CSSProperties;\n\n /**\n * Add custom CSS class names to the generated code block\n */\n className?: string;\n\n /**\n * Add custom CSS class names to the language label\n */\n langClassName?: string;\n\n /**\n * Whether to show the language label\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'pre'\n */\n as?: React.ElementType;\n}\n/**\n * ShikiHighlighter is a React component that provides syntax highlighting for code snippets.\n * It uses Shiki to render beautiful, theme-based syntax highlighting with optimized performance.\n *\n * @example\n * ```tsx\n * <ShikiHighlighter\n * language=\"typescript\"\n * theme=\"github-dark\"\n * delay={100}\n * transformers={[customTransformer]}\n * addDefaultStyles={false}\n * className=\"code-block\"\n * langClassName=\"lang-label\"\n * langStyle={{ color: 'blue' }},\n * style={{ fontSize: '1.2rem' }}\n * >\n * {code}\n * </ShikiHighlighter>\n * ```\n */\nexport const ShikiHighlighter = ({\n language,\n theme,\n delay,\n transformers,\n defaultColor,\n cssVariablePrefix,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'pre',\n customLanguages,\n}: ShikiHighlighterProps): React.ReactElement => {\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n defaultColor,\n cssVariablePrefix,\n };\n\n const normalizedCustomLanguages = customLanguages\n ? Array.isArray(customLanguages)\n ? customLanguages\n : [customLanguages]\n : [];\n\n const { isCustom, languageId, displayLanguageId, resolvedLanguage } =\n resolveLanguage(language, normalizedCustomLanguages);\n\n const highlightedCode = useShikiHighlighter(\n code,\n language,\n theme,\n options\n );\n\n return (\n <Element\n data-testid=\"shiki-container\"\n className={clsx(\n 'relative',\n 'not-prose',\n addDefaultStyles && 'defaultStyles',\n className\n )}\n style={style}\n id=\"shiki-container\"\n >\n {showLanguage && language ? (\n <span\n className={clsx('languageLabel', langClassName)}\n style={langStyle}\n id=\"language-label\"\n >\n {isCustom\n ? `${resolvedLanguage?.scopeName.split('.')[1]}`\n : displayLanguageId || languageId}\n </span>\n ) : null}\n {highlightedCode}\n </Element>\n );\n};\n","import {\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport parse from 'html-react-parser';\n\nimport {\n createHighlighter,\n createSingletonShorthands,\n type Highlighter,\n type CodeToHastOptions,\n type CodeOptionsSingleTheme,\n type CodeOptionsMultipleThemes,\n} from 'shiki';\n\nimport type {\n LanguageRegistration,\n ShikiLanguageRegistration,\n} from './extended-types';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState,\n Themes,\n} from './types';\n\nimport {\n removeTabIndexFromPre,\n throttleHighlighting,\n resolveLanguage,\n resolveTheme,\n} from './utils';\n\nconst DEFAULT_THEMES = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n// Use Shiki managed singleton for bundled languages, create and cache a fresh instance for custom languages\nconst bundledHighlighter = createSingletonShorthands(createHighlighter);\nconst customLangHighlighter = new Map<string, Promise<Highlighter>>();\n\nconst getCachedCustomHighlighter = async (\n cacheKey: string,\n customLang: LanguageRegistration,\n themesToLoad: Theme[]\n) => {\n let instance = customLangHighlighter.get(cacheKey);\n\n if (!instance) {\n instance = createHighlighter({\n langs: [customLang as ShikiLanguageRegistration],\n themes: themesToLoad,\n });\n customLangHighlighter.set(cacheKey, instance);\n }\n return instance;\n};\n\n/**\n * A React hook that provides syntax highlighting using Shiki.\n * Supports single theme and multi-theme highlighting, custom themes\n * and languages, custom transformers, and optional throttling.\n *\n * ```ts\n * // Basic Usage\n * const highlightedCode = useShikiHighlighter( code, 'typescript', 'github-dark');\n * ```\n *\n * ```ts\n * // Custom Languages, Transformers, and Delay\n * const highlightedCode = useShikiHighlighter(code, language, theme, {\n * transformers: [customTransformer],\n * delay: 150\n * customLanguages: ['bosque', 'mcfunction']\n * });\n * ```\n *\n * ```ts\n * // Multiple Themes, Custom Languages, Delay, and Custom Transformers\n * const highlightedCode = useShikiHighlighter(\n * code,\n * language,\n * {\n * light: 'github-light',\n * dark: 'github-dark',\n * dim: 'github-dark-dimmed'\n * },\n * {\n * delay: 150,\n * defaultColor: 'dim',\n * transformers: [customTransformer],\n * customLanguages: ['bosque', 'mcfunction']\n * }\n * );\n * ```\n */\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptions = {}\n) => {\n const [highlightedCode, setHighlightedCode] =\n useState<ReactNode | null>(null);\n\n const normalizedCustomLanguages: LanguageRegistration[] =\n options.customLanguages\n ? Array.isArray(options.customLanguages)\n ? options.customLanguages\n : [options.customLanguages]\n : [];\n\n const customLangId = normalizedCustomLanguages\n .map((lang) => lang.name || '')\n .sort()\n .join('-');\n\n const transformers = useMemo(() => {\n return [removeTabIndexFromPre, ...(options.transformers || [])];\n }, [options.transformers]);\n\n const { isMultiTheme, themeId, multiTheme, singleTheme, themesToLoad } =\n useMemo(() => resolveTheme(themeInput), [themeInput]);\n\n const { isCustom, languageId, resolvedLanguage } = useMemo(\n () => resolveLanguage(lang, normalizedCustomLanguages),\n [lang, customLangId]\n );\n\n const cacheKey = `${languageId}-${themeId}`;\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n const buildShikiOptions = (): CodeToHastOptions => {\n const { defaultColor, cssVariablePrefix } = options;\n const commonOptions = { lang: languageId, transformers };\n\n const themeOptions = isMultiTheme\n ? ({\n themes: multiTheme || DEFAULT_THEMES,\n defaultColor,\n cssVariablePrefix,\n } as CodeOptionsMultipleThemes)\n : ({\n theme: singleTheme || DEFAULT_THEMES.dark,\n } as CodeOptionsSingleTheme);\n\n return { ...commonOptions, ...themeOptions };\n };\n\n useEffect(() => {\n let isMounted = true;\n\n const highlightCode = async () => {\n const codeHighlighter =\n isCustom && resolvedLanguage\n ? await getCachedCustomHighlighter(\n cacheKey,\n resolvedLanguage,\n themesToLoad\n )\n : bundledHighlighter;\n\n const highlighterOptions: CodeToHastOptions = buildShikiOptions();\n\n const html = await codeHighlighter.codeToHtml(\n code,\n highlighterOptions\n );\n if (isMounted) {\n setHighlightedCode(parse(html));\n }\n };\n\n if (options.delay) {\n throttleHighlighting(highlightCode, timeoutControl, options.delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [\n code,\n languageId,\n themeId,\n customLangId,\n transformers,\n options.delay,\n options.defaultColor,\n options.cssVariablePrefix,\n ]);\n\n return highlightedCode;\n};\n","import { visit } from 'unist-util-visit';\nimport { bundledLanguages, isSpecialLang } from 'shiki';\nimport type { ShikiTransformer, ThemeRegistrationAny } from 'shiki';\nimport type { Element } from 'hast';\nimport type { Language, Theme, Themes, TimeoutState } from './types';\nimport type { LanguageRegistration } from './extended-types';\n\n/**\n * Type for the HAST node, use to type `node` from react-markdown\n */\nexport type { Element };\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements\n * Sets 'inline' property to true if the <code> is not within a <pre> tag\n *\n * Pass this plugin to the `rehypePlugins` prop of react-markdown\n * You can then access `inline` as a prop in components passed to react-markdown\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return (tree: any): undefined => {\n visit(tree, 'element', (node: Element, _index, parent: Element) => {\n if (node.tagName === 'code' && parent.tagName !== 'pre') {\n node.properties.inline = true;\n }\n });\n };\n}\n\n/**\n * Function to determine if code is inline based on the presence of line breaks\n *\n * @example\n * const isInline = node && isInlineCode(node: Element)\n */\nexport const isInlineCode = (node: Element): boolean => {\n const textContent = (node.children || [])\n .filter((child) => child.type === 'text')\n .map((child) => child.value)\n .join('');\n\n return !textContent.includes('\\n');\n};\n\n/**\n * Shiki transformer to remove tabindex from <pre> elements\n *\n * Consider retaining tabindex for WCAG 3.1 compliance, scrollable code blocks should be focusable\n * https://github.com/shikijs/shiki/issues/428\n * https://www.w3.org/WAI/standards-guidelines/act/rules/0ssw9k/proposed/\n */\nexport const removeTabIndexFromPre: ShikiTransformer = {\n pre(node) {\n if ('properties' in node) {\n node.properties.tabindex = '-1';\n }\n return node;\n },\n};\n\n/**\n * Optionally throttles rapid sequential highlighting operations\n * Exported for testing in __tests__/throttleHighlighting.test.ts\n *\n * @example\n * const timeoutControl = useRef<TimeoutState>({\n * nextAllowedTime: 0,\n * timeoutId: undefined\n * });\n *\n * throttleHighlighting(highlightCode, timeoutControl, 1000);\n */\nexport const throttleHighlighting = (\n performHighlight: () => Promise<void>,\n timeoutControl: React.MutableRefObject<TimeoutState>,\n throttleMs: number\n) => {\n const now = Date.now();\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(0, timeoutControl.current.nextAllowedTime - now);\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight().catch(console.error);\n timeoutControl.current.nextAllowedTime = now + throttleMs;\n }, delay);\n};\n\ntype ResolvedLanguage = {\n isCustom: boolean;\n languageId: string;\n displayLanguageId?: string;\n resolvedLanguage?: LanguageRegistration;\n};\n\n/**\n * Determines whether a language is custom or built-in, normalizes\n * the language identifier and returns the resolved language with metadata\n *\n * @param lang - The language identifier, either as a string name or language object\n * @param customLanguages - Optional array of custom language definitions\n * @returns Object containing:\n * - isCustom: Whether the language requires custom highlighting\n * - languageId: The normalized language identifier\n * - displayLanguageId: The display name for language label\n * - resolvedLanguage: The full language definition if custom\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages: LanguageRegistration[] = []\n): ResolvedLanguage => {\n // Language is custom but not preloaded\n if (lang && typeof lang === 'object') {\n return {\n isCustom: true,\n languageId: lang.name,\n displayLanguageId: lang.name,\n resolvedLanguage: lang,\n };\n }\n\n // Language is a string\n if (typeof lang === 'string') {\n const displayLanguageId = lang;\n\n // Check if its built-in\n if (lang in bundledLanguages || isSpecialLang(lang)) {\n return {\n isCustom: false,\n languageId: lang,\n displayLanguageId,\n };\n }\n\n // Check if it matches a preloaded custom language\n const customMatch = customLanguages.find(\n (cl) =>\n cl.fileTypes?.includes(lang) ||\n cl.scopeName?.split('.')[1] === lang ||\n cl.name?.toLowerCase() === lang.toLowerCase()\n );\n\n if (customMatch) {\n return {\n isCustom: true,\n languageId: customMatch.name,\n displayLanguageId,\n resolvedLanguage: customMatch,\n };\n }\n\n // If unknown, highlight in plaintext but display unknown language\n return {\n isCustom: false,\n languageId: 'plaintext',\n displayLanguageId,\n };\n }\n\n // Fallback\n return {\n isCustom: false,\n languageId: 'plaintext',\n displayLanguageId: 'plaintext',\n };\n};\n\n/**\n * Determines theme configuration and returns the resolved theme with metadata\n * @param themeInput - The theme input, either as a string name or theme object\n * @returns Object containing:\n * - isMultiTheme: If theme input is a multi-theme configuration\n * - themeId: Theme reference identifier\n * - multiTheme: The multi-theme config if it exists\n * - singleTheme: The single theme if it exists\n * - themesToLoad: The themes to load when creating the highlighter\n */\nexport function resolveTheme(themeInput: Theme | Themes): {\n isMultiTheme: boolean;\n themeId: Theme;\n multiTheme?: Themes | ThemeRegistrationAny | null;\n singleTheme?: Theme | undefined;\n themesToLoad: Theme[];\n} {\n const isTextmateTheme =\n typeof themeInput === 'object' &&\n 'tokenColors' in themeInput &&\n Array.isArray(themeInput.tokenColors);\n\n // Assume non textmate objects are multi theme configs\n const isMultiThemeConfig =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme;\n\n const validMultiThemeObj =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme &&\n Object.entries(themeInput).some(\n ([key, value]) =>\n key &&\n value &&\n key.trim() !== '' &&\n value !== '' &&\n (typeof value === 'string' || isTextmateTheme)\n );\n\n if (isMultiThemeConfig) {\n const themeId = validMultiThemeObj\n ? `multi-${Object.values(themeInput)\n .map(\n (theme) =>\n (typeof theme === 'string' ? theme : theme?.name) ||\n 'custom'\n )\n .sort()\n .join('-')}`\n : 'multi-default';\n\n // If config is invalid, return null to handle fallback in `buildShikiOptions()`\n return {\n isMultiTheme: true,\n themeId,\n multiTheme: validMultiThemeObj ? themeInput : null,\n themesToLoad: validMultiThemeObj ? Object.values(themeInput) : [],\n };\n }\n\n return {\n isMultiTheme: false,\n themeId:\n typeof themeInput === 'string'\n ? themeInput\n : themeInput?.name || 'custom',\n singleTheme: themeInput,\n themesToLoad: [themeInput],\n };\n}\n"],"mappings":"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","names":["styleInject","css","insertAt","head","style","styleInject","React","clsx","useEffect","useMemo","useRef","useState","parse","createHighlighter","createSingletonShorthands","visit","bundledLanguages","isSpecialLang","rehypeInlineCodeProperty","tree","node","_index","parent","isInlineCode","child","removeTabIndexFromPre","throttleHighlighting","performHighlight","timeoutControl","throttleMs","now","delay","resolveLanguage","lang","customLanguages","displayLanguageId","customMatch","cl","resolveTheme","themeInput","isTextmateTheme","isMultiThemeConfig","validMultiThemeObj","key","value","theme","DEFAULT_THEMES","bundledHighlighter","createSingletonShorthands","createHighlighter","customLangHighlighter","getCachedCustomHighlighter","cacheKey","customLang","themesToLoad","instance","useShikiHighlighter","code","lang","themeInput","options","highlightedCode","setHighlightedCode","useState","normalizedCustomLanguages","customLangId","transformers","useMemo","removeTabIndexFromPre","isMultiTheme","themeId","multiTheme","singleTheme","resolveTheme","isCustom","languageId","resolvedLanguage","resolveLanguage","timeoutControl","useRef","buildShikiOptions","defaultColor","cssVariablePrefix","commonOptions","themeOptions","useEffect","isMounted","highlightCode","codeHighlighter","highlighterOptions","html","parse","throttleHighlighting","ShikiHighlighter","language","theme","delay","transformers","defaultColor","cssVariablePrefix","addDefaultStyles","style","langStyle","className","langClassName","showLanguage","code","Element","customLanguages","options","normalizedCustomLanguages","isCustom","languageId","displayLanguageId","resolvedLanguage","resolveLanguage","highlightedCode","useShikiHighlighter","React","clsx"]}
|