react-shiki 0.2.4 → 0.3.0

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 CHANGED
@@ -18,6 +18,7 @@ for react using [Shiki](https://shiki.matsu.io/)
18
18
  - [`react-markdown`](#react-markdown)
19
19
  - [Check if code is inline](#check-if-code-is-inline)
20
20
  - [Custom themes](#custom-themes)
21
+ - [Custom transformers](#custom-transformers)
21
22
  - [Performance](#performance)
22
23
  - [Throttling real-time highlighting](#throttling-real-time-highlighting)
23
24
  - [Streaming and LLM chat UI](#streaming-and-llm-chat-ui)
@@ -28,8 +29,9 @@ for react using [Shiki](https://shiki.matsu.io/)
28
29
  - 🖼️ Provides a `ShikiHighlighter` component for highlighting code as children,
29
30
  as well as a `useShikiHighlighter` hook for more flexibility
30
31
  - 🔐 No `dangerouslySetInnerHTML`, output from Shiki is parsed using `html-react-parser`
31
- - 📦 Supports all Shiki languages and themes in addition to
32
+ - 📦 Supports all Shiki languages and themes
32
33
  - 🖌️ Full support for custom TextMate themes in a JavaScript object format
34
+ - 🔧 Supports passing custom Shiki transformers to the highlighter
33
35
  - 🚰 Performant highlighting of streamed code on the client, with optional throttling
34
36
  - 📚 Includes minimal default styles for code blocks
35
37
  - 🚀 Shiki dynamically imports only the languages and themes used on a page,
@@ -136,7 +138,7 @@ export const CodeHighlight = ({
136
138
  const language = match ? match[1] : undefined;
137
139
 
138
140
  <ShikiHighlighter language={language} theme={"houston"} {...props}>
139
- {String(children)}
141
+ {String(children).trim()}
140
142
  </ShikiHighlighter>;
141
143
  };
142
144
  ```
@@ -167,7 +169,7 @@ const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
167
169
 
168
170
  return !isInline ? (
169
171
  <ShikiHighlighter language={language} theme={"houston"} {...props}>
170
- {String(children)}
172
+ {String(children).trim()}
171
173
  </ShikiHighlighter>
172
174
  ) : (
173
175
  <code className={className} {...props}>
@@ -210,7 +212,7 @@ const CodeHighlight = ({
210
212
 
211
213
  return !inline ? (
212
214
  <ShikiHighlighter language={language} theme={"houston"} {...props}>
213
- {String(children)}
215
+ {String(children).trim()}
214
216
  </ShikiHighlighter>
215
217
  ) : (
216
218
  <code className={className} {...props}>
@@ -221,7 +223,7 @@ return !inline ? (
221
223
 
222
224
  ### Custom themes
223
225
 
224
- ```tsx:title=CodeHighlight.tsx
226
+ ```tsx
225
227
  import tokyoNight from '@styles/tokyo-night.mjs';
226
228
 
227
229
  <ShikiHighlighter language="tsx" theme={tokyoNight}>
@@ -229,6 +231,18 @@ import tokyoNight from '@styles/tokyo-night.mjs';
229
231
  </ShikiHighlighter>;
230
232
  ```
231
233
 
234
+ ### Custom transformers
235
+
236
+ ```tsx
237
+ import { customTransformer } from '@utils/customTransformers';
238
+
239
+ <ShikiHighlighter
240
+ language="tsx"
241
+ transformers={[customTransformer]}>
242
+ {String(code).trim()}
243
+ </ShikiHighlighter>;
244
+ ```
245
+
232
246
  ## Performance
233
247
 
234
248
  `react-shiki` supports performance-optimized highlighting on the client.
@@ -248,12 +262,12 @@ const highlightedCode = useShikiHighlighter(code, language, theme, {
248
262
 
249
263
  `react-shiki` can be used to highlight streamed code from LLM responses in real-time.
250
264
 
251
- I use it for an
252
- LLM chatbot UI, it renders markdown and highlights code in memoized chat messages.
265
+ I use it for an LLM chatbot UI, it renders markdown and highlights
266
+ code in memoized chat messages.
253
267
 
254
268
  Using `useShikiHighlighter` hook:
255
269
 
256
- ```tsx title=CodeHighlight.tsx
270
+ ```tsx
257
271
  import type { ReactNode } from "react";
258
272
  import { isInlineCode, useShikiHighlighter, type Element } from "react-shiki";
259
273
  import tokyoNight from "@styles/tokyo-night.mjs";
@@ -270,7 +284,7 @@ export const CodeHighlight = ({
270
284
  node,
271
285
  ...props
272
286
  }: CodeHighlightProps) => {
273
- const code = String(children);
287
+ const code = String(children).trim();
274
288
  const language = className?.match(/language-(\w+)/)?.[1];
275
289
 
276
290
  const isInline = node ? isInlineCode(node) : false;
@@ -304,7 +318,7 @@ export const CodeHighlight = ({
304
318
 
305
319
  Or using the `ShikiHighlighter` component:
306
320
 
307
- ```tsx title=CodeHighlight.tsx
321
+ ```tsx
308
322
  import type { ReactNode } from "react";
309
323
  import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
310
324
 
@@ -332,7 +346,7 @@ export const CodeHighlight = ({
332
346
  delay={150}
333
347
  {...props}
334
348
  >
335
- {String(children)}
349
+ {String(children).trim()}
336
350
  </ShikiHighlighter>
337
351
  ) : (
338
352
  <code className={className} {...props}>
@@ -344,7 +358,7 @@ export const CodeHighlight = ({
344
358
 
345
359
  Passed to `react-markdown` as a `code` component in memo-ized chat messages:
346
360
 
347
- ```tsx title=ChatMessages.tsx
361
+ ```tsx
348
362
  const RenderedMessage = React.memo(({ message }: { message: Message }) => (
349
363
  <div className={cn(messageStyles[message.role])}>
350
364
  <ReactMarkdown components={{ code: CodeHighlight }}>
@@ -0,0 +1,140 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { BundledLanguage, SpecialLanguage, ThemeRegistration, BundledTheme, ShikiTransformer } from 'shiki';
3
+ import { Root, Element } from 'hast';
4
+ export { Element } from 'hast';
5
+
6
+ /**
7
+ * Languages for syntax highlighting.
8
+ * @see https://shiki.style/languages
9
+ */
10
+ type Language = BundledLanguage | SpecialLanguage | (string & {}) | undefined;
11
+ /**
12
+ * A textmate theme object or a Shiki BundledTheme
13
+ * @see https://shiki.style/themes
14
+ */
15
+ type Theme = ThemeRegistration | BundledTheme;
16
+ /**
17
+ * Configuration options for the syntax highlighter
18
+ */
19
+ type HighlighterOptions = {
20
+ /**
21
+ * Minimum time (in milliseconds) between highlight operations.
22
+ * @default undefined (no throttling)
23
+ */
24
+ delay?: number;
25
+ /**
26
+ * Custom Shiki transformers to apply to the highlighted code.
27
+ */
28
+ transformers?: ShikiTransformer[];
29
+ };
30
+
31
+ /**
32
+ * Props for the ShikiHighlighter component
33
+ */
34
+ interface ShikiHighlighterProps extends HighlighterOptions {
35
+ /**
36
+ * The programming language for syntax highlighting
37
+ * @see https://shiki.style/languages
38
+ */
39
+ language: Language;
40
+ /**
41
+ * The code to be highlighted
42
+ */
43
+ children: string;
44
+ /**
45
+ * The color theme for syntax highlighting
46
+ * Supports Shiki and custom textmate themes
47
+ * @see https://shiki.style/themes
48
+ */
49
+ theme: Theme;
50
+ /**
51
+ * The delay in milliseconds between streamed updates
52
+ * Use this when highlighting code being streamed on the client
53
+ * @default undefined (no delay)
54
+ */
55
+ delay?: number;
56
+ /**
57
+ * Pass custom Shiki transformers to the highlighter
58
+ */
59
+ transformers?: ShikiTransformer[];
60
+ /**
61
+ * Controls the application of default styles to the generated code blocks
62
+ *
63
+ * Default styles include padding, overflow handling, border radius, language label styling, and font settings
64
+ * @default true
65
+ */
66
+ addDefaultStyles?: boolean;
67
+ /**
68
+ * Add custom inline styles to the generated code block
69
+ */
70
+ style?: React.CSSProperties;
71
+ /**
72
+ * Add custom inline styles to the language label
73
+ */
74
+ langStyle?: React.CSSProperties;
75
+ /**
76
+ * Add custom CSS class names to the generated code block
77
+ */
78
+ className?: string;
79
+ /**
80
+ * Add custom CSS class names to the language label
81
+ */
82
+ langClassName?: string;
83
+ /**
84
+ * Whether to show the language label
85
+ * @default true
86
+ */
87
+ showLanguage?: boolean;
88
+ /**
89
+ * The HTML element that wraps the generated code block.
90
+ * @default 'pre'
91
+ */
92
+ as?: React.ElementType;
93
+ }
94
+ /**
95
+ * ShikiHighlighter is a React component that provides syntax highlighting for code snippets.
96
+ * It uses Shiki to render beautiful, theme-based syntax highlighting with optimized performance.
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * <ShikiHighlighter
101
+ * language="typescript"
102
+ * theme="github-dark"
103
+ * delay={100} // Optional throttling for streamed updates
104
+ * >
105
+ * {code}
106
+ * </ShikiHighlighter>
107
+ * ```
108
+ */
109
+ declare const ShikiHighlighter: ({ language, theme, delay, transformers, addDefaultStyles, style, langStyle, className, langClassName, showLanguage, children: code, as: Element, }: ShikiHighlighterProps) => React.ReactElement;
110
+
111
+ /**
112
+ * A React hook that provides syntax highlighting using Shiki.
113
+ * Supports optional throttled highlights and custom themes.
114
+ *
115
+ * @example
116
+ * const highlightedCode = useShikiHighlighter(code, language, theme, {
117
+ * transformers: [removeTabIndexFromPre],
118
+ * delay: 150
119
+ * });
120
+ */
121
+ declare const useShikiHighlighter: (code: string, lang: Language, theme: Theme, options?: HighlighterOptions) => ReactNode;
122
+
123
+ /**
124
+ * Rehype plugin to add an 'inline' property to <code> elements.
125
+ * Sets an 'inline' property to true if the <code> is not within a <pre> tag.
126
+ *
127
+ * Pass this plugin to the `rehypePlugins` prop of ReactMarkdown.
128
+ * You can then access `inline` as a prop from ReactMarkdown.
129
+ *
130
+ * @example
131
+ * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />
132
+ */
133
+ declare function rehypeInlineCodeProperty(): (tree: Root) => undefined;
134
+ /**
135
+ * Function to determine if code is inline based on the presence of line breaks.
136
+ * Reports `inline = true` for single line fenced code blocks.
137
+ */
138
+ declare const isInlineCode: (node: Element) => boolean;
139
+
140
+ export { ShikiHighlighter as default, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- function c(e,{insertAt:t}={}){if(!e||typeof document>"u")return;let i=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",t==="top"&&i.firstChild?i.insertBefore(r,i.firstChild):i.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}c(`.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
- `);import y from"react";import{clsx as x}from"clsx";import{useEffect as b,useRef as w,useState as k}from"react";import E from"html-react-parser";import{createHighlighter as L,ShikiError as f}from"shiki";import{visit as T}from"unist-util-visit";function S(){return function(e){T(e,"element",function(t,i,r){r&&r.tagName==="pre"?t.properties.inline=!1:t.properties.inline=!0})}}var H=e=>e.position?.start.line===e.position?.end.line,u={pre(e){return"properties"in e&&(e.properties.tabindex="-1"),e}};var d=null,P=async e=>{d||(d=L({themes:[],langs:[]}));let t=await d;return await R(t,e),t},R=async(e,t)=>{try{await e.loadTheme(t)}catch(i){throw i instanceof f&&console.warn("Error loading theme:",i.message),i}},C=async(e,t)=>{let i=t??"plaintext";try{return await e.loadLanguage(i),i}catch(r){return r instanceof f&&r.message.includes("not included in this bundle")&&console.warn(`Language '${i}' not supported, falling back to plaintext`),"plaintext"}},I=(e,t,i)=>{let r=Date.now();clearTimeout(t.current.timeoutId);let n=Math.max(0,t.current.nextAllowedTime-r);t.current.timeoutId=setTimeout(()=>{e().catch(console.error),t.current.nextAllowedTime=r+i},n)},p=(e,t,i,r={})=>{let[n,h]=k(null),o=w({nextAllowedTime:0,timeoutId:void 0});return b(()=>{let a=!0,l=async()=>{let s=await P(i),m=await C(s,t),g=s.codeToHtml(e,{lang:m,theme:i,transformers:[u]});a&&h(E(g))};return r.delay?I(l,o,r.delay):l().catch(console.error),()=>{a=!1,clearTimeout(o.current.timeoutId)}},[e,t]),n};var v=({language:e,theme:t,delay:i,addDefaultStyles:r=!0,style:n,langStyle:h,className:o,langClassName:a,showLanguage:l=!0,children:s,as:m="pre"})=>{let g=p(s,e,t,{delay:i});return y.createElement(m,{className:x("relative","not-prose",r&&"defaultStyles",o),style:n,id:"shiki-container"},l&&e?y.createElement("span",{className:x("languageLabel",a),style:h,id:"language-label"},e):null,g)};export{v as default,H as isInlineCode,S as rehypeInlineCodeProperty,p as useShikiHighlighter};
1
+ function h(e,{insertAt:i}={}){if(!e||typeof document>"u")return;let r=document.head||document.getElementsByTagName("head")[0],t=document.createElement("style");t.type="text/css",i==="top"&&r.firstChild?r.insertBefore(t,r.firstChild):r.appendChild(t),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e))}h(`.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
+ `);import x from"react";import{clsx as S}from"clsx";import{useEffect as E,useRef as I,useState as v}from"react";import R from"html-react-parser";import{createHighlighter as N,createSingletonShorthands as P}from"shiki";import{visit as L}from"unist-util-visit";import{bundledLanguages as b,isSpecialLang as H}from"shiki";function C(){return function(e){L(e,"element",function(i,r,t){i.tagName==="code"&&t.tagName!=="pre"&&(i.properties.inline=!0)})}}var k=e=>!(e.children||[]).filter(r=>r.type==="text").map(r=>r.value).join("").includes(`
3
+ `),c={pre(e){return"properties"in e&&(e.properties.tabindex="-1"),e}},f=(e,i,r)=>{let t=Date.now();clearTimeout(i.current.timeoutId);let o=Math.max(0,i.current.nextAllowedTime-t);i.current.timeoutId=setTimeout(()=>{e().catch(console.error),i.current.nextAllowedTime=t+r},o)},y=e=>typeof e=="string"&&!(e in b)&&!H(e)?"plaintext":e;var w=P(N),u=(e,i,r,t={})=>{let[o,m]=v(null),p=y(i),n=I({nextAllowedTime:0,timeoutId:void 0});return E(()=>{let a=!0,g=[c,...t.transformers||[]],s=async()=>{let d=await w.codeToHtml(e,{lang:p,theme:r,transformers:g});a&&m(R(d))},{delay:l}=t;return l?f(s,n,l):s().catch(console.error),()=>{a=!1,clearTimeout(n.current.timeoutId)}},[e,i]),o};var B=({language:e,theme:i,delay:r,transformers:t,addDefaultStyles:o=!0,style:m,langStyle:p,className:n,langClassName:a,showLanguage:g=!0,children:s,as:l="pre"})=>{let T=u(s,e,i,{delay:r,transformers:t});return x.createElement(l,{"data-testid":"shiki-container",className:S("relative","not-prose",o&&"defaultStyles",n),style:m,id:"shiki-container"},g&&e?x.createElement("span",{className:S("languageLabel",a),style:p,id:"language-label"},e):null,T)};export{B as default,k as isInlineCode,C as rehypeInlineCodeProperty,u as useShikiHighlighter};
3
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';\nimport React from 'react';\nimport { clsx } from 'clsx';\nimport { useShikiHighlighter } from './useShiki';\nimport type { Language, Theme, HighlighterOptions } from './types';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /** \n * The programming language for syntax highlighting\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 for syntax highlighting\n * Supports Shiki and custom textmate themes\n * @see https://shiki.style/themes\n */\n theme: Theme;\n\n /** \n * The delay in milliseconds between streamed updates\n * Use this when highlighting code being streamed on the client\n * @default undefined (no delay)\n */\n delay?: number;\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/**\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} // Optional throttling for streamed updates\n * >\n * {code}\n * </ShikiHighlighter>\n * ```\n */\nexport const ShikiHighlighter = ({\n language,\n theme,\n delay,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'pre',\n}: ShikiHighlighterProps): React.ReactElement => {\n const highlightedCode = useShikiHighlighter(code, language, theme, { delay });\n\n return (\n <Element\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 {language}\n </span>\n ) : null}\n {highlightedCode}\n </Element>\n );\n};\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport parse from 'html-react-parser';\n\nimport {\n createHighlighter,\n ShikiError,\n type BundledLanguage,\n type Highlighter\n} from 'shiki';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState\n} from './types';\n\nimport { removeTabIndexFromPre } from './utils';\n\n\n/**\n* Singleton highlighter instance\n*/\nlet highlighterPromise: Promise<Highlighter> | null = null;\n\n\n/**\n* Creates or returns the singleton highlighter instance\n*/\nconst makeHighlighter = async (theme: Theme): Promise<Highlighter> => {\n if (!highlighterPromise) {\n highlighterPromise = createHighlighter({\n themes: [],\n langs: []\n });\n }\n\n const highlighter = await highlighterPromise;\n await loadTheme(highlighter, theme);\n return highlighter;\n};\n\n\n/**\n* Loads a theme dynamically if it hasn't been loaded yet\n*/\nconst loadTheme = async (\n highlighter: Highlighter,\n theme: Theme\n): Promise<void> => {\n try {\n await highlighter.loadTheme(theme);\n } catch (error) {\n if (error instanceof ShikiError) {\n console.warn('Error loading theme:', error.message);\n }\n throw error;\n }\n};\n\n\n/**\n* Loads a language dynamically, falling back to plaintext if not available\n*/\nconst loadLanguage = async (\n highlighter: Highlighter,\n lang: Language\n): Promise<string> => {\n const resolvedLanguage = lang ?? 'plaintext';\n\n try {\n await highlighter.loadLanguage(resolvedLanguage as BundledLanguage);\n return resolvedLanguage;\n } catch (error) {\n if (error instanceof ShikiError && error.message.includes('not included in this bundle')) {\n console.warn(`Language '${resolvedLanguage}' not supported, falling back to plaintext`);\n }\n return 'plaintext';\n }\n};\n\n\n/**\n* Optionally throttles rapid sequential highlighting operations to conserve resources\n* Exported for testing in __tests__/throttleHighlighting.test.ts\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\n/**\n* A React hook that provides syntax highlighting using Shiki. Uses a\n* singleton highlighter instance and supports optional throttled highlights\n*/\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n theme: Theme,\n options: HighlighterOptions = {}\n): ReactNode | null => {\n const [highlightedCode, setHighlightedCode] = useState<ReactNode | null>(null);\n const delayRef = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined\n });\n\n useEffect(() => {\n let isMounted = true;\n\n const highlightCode = async () => {\n const highlighter = await makeHighlighter(theme);\n const language = await loadLanguage(highlighter, lang);\n\n const html = highlighter.codeToHtml(code, {\n lang: language,\n theme,\n transformers: [removeTabIndexFromPre],\n });\n\n if (isMounted) {\n setHighlightedCode(parse(html));\n }\n };\n\n if (options.delay) {\n throttleHighlighting(highlightCode, delayRef, options.delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(delayRef.current.timeoutId);\n };\n }, [code, lang]);\n\n return highlightedCode;\n};\n","import type { ShikiTransformer } from 'shiki';\nimport type { Element, Root } from 'hast';\nimport { visit } from 'unist-util-visit';\n\n/**\n * This augmentation ensures that hast.RootData includes the same\n * index signature as unist.Data, preventing type incompatibilities\n * when we pass a hast.Root to unist-util-visit (which expects Node<Data>).\n */\ndeclare module 'hast' {\n interface RootData {\n [key: string]: unknown;\n }\n}\n\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements.\n * Sets an 'inline' property to true if the <code> is not within a <pre> tag.\n *\n * Pass this plugin to the `rehypePlugins` prop of ReactMarkdown.\n * You can then access `inline` as a prop from ReactMarkdown.\n *\n * @example\n * import ReactMarkdown from 'react-markdown';\n * import { rehypeInlineCodeProperty } from 'react-shiki';\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return function (tree: Root): undefined {\n visit(tree as any, 'element', function (node: Element, _index, parent: Element) {\n if (parent && parent.tagName === 'pre') {\n node.properties.inline = false;\n } else {\n node.properties.inline = true;\n }\n });\n }\n}\n\n\n/**\n * Less accurate way to determine if code is inline.\n * Reports `inline = false` for multiline inline code blocks.\n */\nexport const isInlineCode = (node: Element): boolean => {\n return node.position?.start.line === node.position?.end.line;\n};\n\n\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\nexport type { Element };\n"],"mappings":"AACyB,SAARA,EAA6BC,EAAK,CAAE,SAAAC,CAAS,EAAI,CAAC,EAAG,CAC1D,GAAI,CAACD,GAAO,OAAO,SAAa,IAAa,OAE7C,IAAME,EAAO,SAAS,MAAQ,SAAS,qBAAqB,MAAM,EAAE,CAAC,EAC/DC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,KAAO,WAETF,IAAa,OACXC,EAAK,WACPA,EAAK,aAAaC,EAAOD,EAAK,UAAU,EAK1CA,EAAK,YAAYC,CAAK,EAGpBA,EAAM,WACRA,EAAM,WAAW,QAAUH,EAE3BG,EAAM,YAAY,SAAS,eAAeH,CAAG,CAAC,CAElD,CCvB8BI,EAAY;AAAA,CAAiP,ECCrS,OAAOC,MAAW,QAClB,OAAS,QAAAC,MAAY,OCFrB,OAAS,aAAAC,EAAW,UAAAC,EAAQ,YAAAC,MAAgC,QAC5D,OAAOC,MAAW,oBAElB,OACE,qBAAAC,EACA,cAAAC,MAGK,QCNP,OAAS,SAAAC,MAAa,mBA0Bf,SAASC,GAA2B,CACzC,OAAO,SAAUC,EAAuB,CACtCF,EAAME,EAAa,UAAW,SAAUC,EAAeC,EAAQC,EAAiB,CAC1EA,GAAUA,EAAO,UAAY,MAC/BF,EAAK,WAAW,OAAS,GAEzBA,EAAK,WAAW,OAAS,EAE7B,CAAC,CACH,CACF,CAOO,IAAMG,EAAgBH,GACpBA,EAAK,UAAU,MAAM,OAASA,EAAK,UAAU,IAAI,KAK7CI,EAA0C,CACrD,IAAIJ,EAAM,CACR,MAAI,eAAgBA,IAClBA,EAAK,WAAW,SAAW,MAEtBA,CACT,CACF,EDnCA,IAAIK,EAAkD,KAMhDC,EAAkB,MAAOC,GAAuC,CAC/DF,IACHA,EAAqBG,EAAkB,CACrC,OAAQ,CAAC,EACT,MAAO,CAAC,CACV,CAAC,GAGH,IAAMC,EAAc,MAAMJ,EAC1B,aAAMK,EAAUD,EAAaF,CAAK,EAC3BE,CACT,EAMMC,EAAY,MAChBD,EACAF,IACkB,CAClB,GAAI,CACF,MAAME,EAAY,UAAUF,CAAK,CACnC,OAASI,EAAO,CACd,MAAIA,aAAiBC,GACnB,QAAQ,KAAK,uBAAwBD,EAAM,OAAO,EAE9CA,CACR,CACF,EAMME,EAAe,MACnBJ,EACAK,IACoB,CACpB,IAAMC,EAAmBD,GAAQ,YAEjC,GAAI,CACF,aAAML,EAAY,aAAaM,CAAmC,EAC3DA,CACT,OAASJ,EAAO,CACd,OAAIA,aAAiBC,GAAcD,EAAM,QAAQ,SAAS,6BAA6B,GACrF,QAAQ,KAAK,aAAaI,CAAgB,4CAA4C,EAEjF,WACT,CACF,EAOaC,EAAuB,CAClCC,EACAC,EACAC,IACG,CACH,IAAMC,EAAM,KAAK,IAAI,EACrB,aAAaF,EAAe,QAAQ,SAAS,EAE7C,IAAMG,EAAQ,KAAK,IAAI,EAAGH,EAAe,QAAQ,gBAAkBE,CAAG,EACtEF,EAAe,QAAQ,UAAY,WAAW,IAAM,CAClDD,EAAiB,EAAE,MAAM,QAAQ,KAAK,EACtCC,EAAe,QAAQ,gBAAkBE,EAAMD,CACjD,EAAGE,CAAK,CACV,EAMaC,EAAsB,CACjCC,EACAT,EACAP,EACAiB,EAA8B,CAAC,IACV,CACrB,GAAM,CAACC,EAAiBC,CAAkB,EAAIC,EAA2B,IAAI,EACvEC,EAAWC,EAAqB,CACpC,gBAAiB,EACjB,UAAW,MACb,CAAC,EAED,OAAAC,EAAU,IAAM,CACd,IAAIC,EAAY,GAEVC,EAAgB,SAAY,CAChC,IAAMvB,EAAc,MAAMH,EAAgBC,CAAK,EACzC0B,EAAW,MAAMpB,EAAaJ,EAAaK,CAAI,EAE/CoB,EAAOzB,EAAY,WAAWc,EAAM,CACxC,KAAMU,EACN,MAAA1B,EACA,aAAc,CAAC4B,CAAqB,CACtC,CAAC,EAEGJ,GACFL,EAAmBU,EAAMF,CAAI,CAAC,CAElC,EAEA,OAAIV,EAAQ,MACVR,EAAqBgB,EAAeJ,EAAUJ,EAAQ,KAAK,EAE3DQ,EAAc,EAAE,MAAM,QAAQ,KAAK,EAG9B,IAAM,CACXD,EAAY,GACZ,aAAaH,EAAS,QAAQ,SAAS,CACzC,CACF,EAAG,CAACL,EAAMT,CAAI,CAAC,EAERW,CACT,EDzDO,IAAMY,EAAmB,CAAC,CAC/B,SAAAC,EACA,MAAAC,EACA,MAAAC,EACA,iBAAAC,EAAmB,GACnB,MAAAC,EACA,UAAAC,EACA,UAAAC,EACA,cAAAC,EACA,aAAAC,EAAe,GACf,SAAUC,EACV,GAAIC,EAAU,KAChB,IAAiD,CAC/C,IAAMC,EAAkBC,EAAoBH,EAAMT,EAAUC,EAAO,CAAE,MAAAC,CAAM,CAAC,EAE5E,OACEW,EAAA,cAACH,EAAA,CACC,UAAWI,EACT,WACA,YACAX,GAAoB,gBACpBG,CACF,EACA,MAAOF,EACP,GAAG,mBAEFI,GAAgBR,EACfa,EAAA,cAAC,QACC,UAAWC,EAAK,gBAAiBP,CAAa,EAC9C,MAAOF,EACP,GAAG,kBAEFL,CACH,EACE,KACHW,CACH,CAEJ","names":["styleInject","css","insertAt","head","style","styleInject","React","clsx","useEffect","useRef","useState","parse","createHighlighter","ShikiError","visit","rehypeInlineCodeProperty","tree","node","_index","parent","isInlineCode","removeTabIndexFromPre","highlighterPromise","makeHighlighter","theme","createHighlighter","highlighter","loadTheme","error","ShikiError","loadLanguage","lang","resolvedLanguage","throttleHighlighting","performHighlight","timeoutControl","throttleMs","now","delay","useShikiHighlighter","code","options","highlightedCode","setHighlightedCode","useState","delayRef","useRef","useEffect","isMounted","highlightCode","language","html","removeTabIndexFromPre","parse","ShikiHighlighter","language","theme","delay","addDefaultStyles","style","langStyle","className","langClassName","showLanguage","code","Element","highlightedCode","useShikiHighlighter","React","clsx"]}
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';\nimport React from 'react';\nimport { clsx } from 'clsx';\nimport { useShikiHighlighter } from './useShiki';\nimport type { Language, Theme, HighlighterOptions } from './types';\nimport type { ShikiTransformer } from 'shiki';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /** \n * The programming language for syntax highlighting\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 for syntax highlighting\n * Supports Shiki and custom textmate themes\n * @see https://shiki.style/themes\n */\n theme: Theme;\n\n /** \n * The delay in milliseconds between streamed updates\n * Use this when highlighting code being streamed on the client\n * @default undefined (no delay)\n */\n delay?: number;\n\n /**\n * Pass custom Shiki transformers to the highlighter\n */\n transformers?: ShikiTransformer[];\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/**\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} // Optional throttling for streamed updates\n * >\n * {code}\n * </ShikiHighlighter>\n * ```\n */\nexport const ShikiHighlighter = ({\n language,\n theme,\n delay,\n transformers,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'pre',\n}: ShikiHighlighterProps): React.ReactElement => {\n const options: HighlighterOptions = { delay, transformers };\n const highlightedCode = useShikiHighlighter(code, language, theme, options);\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 {language}\n </span>\n ) : null}\n {highlightedCode}\n </Element>\n );\n};\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport parse from 'html-react-parser';\n\nimport {\n createHighlighter,\n createSingletonShorthands,\n} from 'shiki';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState\n} from './types';\n\nimport { \n removeTabIndexFromPre,\n throttleHighlighting,\n resolvedLang\n} from './utils';\n\n\n/**\n * Create singleton shorthands which handle automatic loading\n * of the languages and themes, with support for custom themes.\n */\nconst highlighter = createSingletonShorthands(createHighlighter);\n\n\n/**\n * A React hook that provides syntax highlighting using Shiki.\n * Supports optional throttled highlights and custom themes.\n *\n * @example\n * const highlightedCode = useShikiHighlighter(code, language, theme, {\n * transformers: [removeTabIndexFromPre],\n * delay: 150\n * });\n */\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n theme: Theme,\n options: HighlighterOptions = {}\n) => {\n const [highlightedCode, setHighlightedCode] = useState<ReactNode | null>(null);\n const language = resolvedLang(lang);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined\n });\n\n useEffect(() => {\n let isMounted = true;\n\n const transformers = [removeTabIndexFromPre, ...(options.transformers || [])];\n\n const highlightCode = async () => {\n const html = await highlighter.codeToHtml(code, {\n lang: language,\n theme,\n transformers\n });\n\n if (isMounted) {\n setHighlightedCode(parse(html));\n }\n };\n\n const { delay } = options;\n\n if (delay) {\n throttleHighlighting(highlightCode, timeoutControl, delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [code, lang]);\n\n return highlightedCode;\n};\n\n","import { visit } from 'unist-util-visit';\nimport { bundledLanguages, isSpecialLang, } from 'shiki';\nimport type { BundledLanguage, ShikiTransformer } from 'shiki';\nimport type { Element, Root } from 'hast';\nimport type { Language, TimeoutState } from './types';\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements.\n * Sets an 'inline' property to true if the <code> is not within a <pre> tag.\n *\n * Pass this plugin to the `rehypePlugins` prop of ReactMarkdown.\n * You can then access `inline` as a prop from ReactMarkdown.\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return function (tree: Root): undefined {\n visit(tree as any, 'element', function (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/**\n * Function to determine if code is inline based on the presence of line breaks.\n * Reports `inline = true` for single line fenced code blocks.\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/**\n * Shiki transformer to remove tabindex from <pre> elements.\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/**\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\n/**\n * Resolve the requested language to a bundled language.\n * If the language is not special or not included in Shiki’s bundledLanguages,\n * fall back to \"plaintext\".\n *\n * @returns {BundledLanguage} The resolved language.\n */\nexport const resolvedLang = (lang: Language): BundledLanguage => {\n if (typeof lang === 'string') {\n if (!(lang in bundledLanguages) && !isSpecialLang(lang)) {\n return 'plaintext' as BundledLanguage;\n }\n return lang as BundledLanguage;\n }\n return lang as Language as BundledLanguage;\n};\n\nexport type { Element };\n"],"mappings":"AACyB,SAARA,EAA6BC,EAAK,CAAE,SAAAC,CAAS,EAAI,CAAC,EAAG,CAC1D,GAAI,CAACD,GAAO,OAAO,SAAa,IAAa,OAE7C,IAAME,EAAO,SAAS,MAAQ,SAAS,qBAAqB,MAAM,EAAE,CAAC,EAC/DC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,KAAO,WAETF,IAAa,OACXC,EAAK,WACPA,EAAK,aAAaC,EAAOD,EAAK,UAAU,EAK1CA,EAAK,YAAYC,CAAK,EAGpBA,EAAM,WACRA,EAAM,WAAW,QAAUH,EAE3BG,EAAM,YAAY,SAAS,eAAeH,CAAG,CAAC,CAElD,CCvB8BI,EAAY;AAAA,CAAiP,ECCrS,OAAOC,MAAW,QAClB,OAAS,QAAAC,MAAY,OCFrB,OAAS,aAAAC,EAAW,UAAAC,EAAQ,YAAAC,MAAgC,QAC5D,OAAOC,MAAW,oBAElB,OACE,qBAAAC,EACA,6BAAAC,MACK,QCNP,OAAS,SAAAC,MAAa,mBACtB,OAAS,oBAAAC,EAAkB,iBAAAC,MAAsB,QAe1C,SAASC,GAA2B,CACzC,OAAO,SAAUC,EAAuB,CACtCJ,EAAMI,EAAa,UAAW,SAAUC,EAAeC,EAAQC,EAAiB,CAC1EF,EAAK,UAAY,QAAUE,EAAO,UAAY,QAChDF,EAAK,WAAW,OAAS,GAE7B,CAAC,CACH,CACF,CAOO,IAAMG,EAAgBH,GAMpB,EALcA,EAAK,UAAY,CAAC,GACpC,OAAQI,GAAUA,EAAM,OAAS,MAAM,EACvC,IAAKA,GAAUA,EAAM,KAAK,EAC1B,KAAK,EAAE,EAEU,SAAS;AAAA,CAAI,EAOtBC,EAA0C,CACrD,IAAIL,EAAM,CACR,MAAI,eAAgBA,IAClBA,EAAK,WAAW,SAAW,MAEtBA,CACT,CACF,EAeaM,EAAuB,CAClCC,EACAC,EACAC,IACG,CACH,IAAMC,EAAM,KAAK,IAAI,EACrB,aAAaF,EAAe,QAAQ,SAAS,EAE7C,IAAMG,EAAQ,KAAK,IAAI,EAAGH,EAAe,QAAQ,gBAAkBE,CAAG,EACtEF,EAAe,QAAQ,UAAY,WAAW,IAAM,CAClDD,EAAiB,EAAE,MAAM,QAAQ,KAAK,EACtCC,EAAe,QAAQ,gBAAkBE,EAAMD,CACjD,EAAGE,CAAK,CACV,EASaC,EAAgBC,GACvB,OAAOA,GAAS,UACd,EAAEA,KAAQjB,IAAqB,CAACC,EAAcgB,CAAI,EAC7C,YAIJA,EDrET,IAAMC,EAAcC,EAA0BC,CAAiB,EAalDC,EAAsB,CACjCC,EACAC,EACAC,EACAC,EAA8B,CAAC,IAC5B,CACH,GAAM,CAACC,EAAiBC,CAAkB,EAAIC,EAA2B,IAAI,EACvEC,EAAWC,EAAaP,CAAI,EAE5BQ,EAAiBC,EAAqB,CAC1C,gBAAiB,EACjB,UAAW,MACb,CAAC,EAED,OAAAC,EAAU,IAAM,CACd,IAAIC,EAAY,GAEVC,EAAe,CAACC,EAAuB,GAAIX,EAAQ,cAAgB,CAAC,CAAE,EAEtEY,EAAgB,SAAY,CAChC,IAAMC,EAAO,MAAMpB,EAAY,WAAWI,EAAM,CAC9C,KAAMO,EACN,MAAAL,EACA,aAAAW,CACF,CAAC,EAEGD,GACFP,EAAmBY,EAAMD,CAAI,CAAC,CAElC,EAEM,CAAE,MAAAE,CAAM,EAAIf,EAElB,OAAIe,EACFC,EAAqBJ,EAAeN,EAAgBS,CAAK,EAEzDH,EAAc,EAAE,MAAM,QAAQ,KAAK,EAG9B,IAAM,CACXH,EAAY,GACZ,aAAaH,EAAe,QAAQ,SAAS,CAC/C,CACF,EAAG,CAACT,EAAMC,CAAI,CAAC,EAERG,CACT,EDYO,IAAMgB,EAAmB,CAAC,CAC/B,SAAAC,EACA,MAAAC,EACA,MAAAC,EACA,aAAAC,EACA,iBAAAC,EAAmB,GACnB,MAAAC,EACA,UAAAC,EACA,UAAAC,EACA,cAAAC,EACA,aAAAC,EAAe,GACf,SAAUC,EACV,GAAIC,EAAU,KAChB,IAAiD,CAE/C,IAAMC,EAAkBC,EAAoBH,EAAMV,EAAUC,EADxB,CAAE,MAAAC,EAAO,aAAAC,CAAa,CACgB,EAE1E,OACEW,EAAA,cAACH,EAAA,CACC,cAAY,kBACZ,UAAWI,EACT,WACA,YACAX,GAAoB,gBACpBG,CACF,EACA,MAAOF,EACP,GAAG,mBAEFI,GAAgBT,EACfc,EAAA,cAAC,QACC,UAAWC,EAAK,gBAAiBP,CAAa,EAC9C,MAAOF,EACP,GAAG,kBAEFN,CACH,EACE,KACHY,CACH,CAEJ","names":["styleInject","css","insertAt","head","style","styleInject","React","clsx","useEffect","useRef","useState","parse","createHighlighter","createSingletonShorthands","visit","bundledLanguages","isSpecialLang","rehypeInlineCodeProperty","tree","node","_index","parent","isInlineCode","child","removeTabIndexFromPre","throttleHighlighting","performHighlight","timeoutControl","throttleMs","now","delay","resolvedLang","lang","highlighter","createSingletonShorthands","createHighlighter","useShikiHighlighter","code","lang","theme","options","highlightedCode","setHighlightedCode","useState","language","resolvedLang","timeoutControl","useRef","useEffect","isMounted","transformers","removeTabIndexFromPre","highlightCode","html","parse","delay","throttleHighlighting","ShikiHighlighter","language","theme","delay","transformers","addDefaultStyles","style","langStyle","className","langClassName","showLanguage","code","Element","highlightedCode","useShikiHighlighter","React","clsx"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-shiki",
3
3
  "description": "Syntax highlighter component for react using shiki",
4
- "version": "0.2.4",
4
+ "version": "0.3.0",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Bassim Shahidy",