Typescript

Include Typescript compiler's information in your code blocks using Typescript Twoslash.

content.md
```ts
const hi = "Hello"
const msg = `${hi}, world`
// ^?
// @errors: 2588
msg = 123
```
const = "Hello"
const = `${}, world`
const msg: "Hello, world"
= 123
Cannot assign to 'msg' because it is a constant.

Implementation

We use the twoslash library to extract the typescript compiler's information from the code block. For more information, check the twoslash docs.

npm i twoslash

In the Code component we run typescript and transform twoslash queries and hovers into Code Hike annotations, that then use handlers similar to Callout and Tooltip.

code.tsx
import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
import { createTwoslasher } from "twoslash"
const twoslasher = createTwoslasher({
fsMap: new Map(),
compilerOptions: {},
})
async function Code({ codeblock }: { codeblock: RawCode }) {
// run typescript:
const { hovers, code, queries, errors } = twoslasher(
codeblock.value,
codeblock.lang,
)
// highlight code:
const highlighted = await highlight(
{ ...codeblock, value: code },
"github-dark",
)
hovers.forEach(({ text, line, character, length }) => {
highlighted.annotations.push({
name: "tooltip",
query: text,
lineNumber: line + 1,
fromColumn: character + 1,
toColumn: character + length,
})
})
queries.forEach(({ text, line, character }) => {
highlighted.annotations.push({
name: "callout",
query: text,
fromLineNumber: line + 1,
toLineNumber: line + 1,
data: { character },
})
})
errors.forEach(({ text, line, character }) => {
highlighted.annotations.push({
name: "callout",
query: text,
fromLineNumber: line + 1,
toLineNumber: line + 1,
data: { character, className: "text-red-400" },
})
})
return (
<Pre
className=""
code={highlighted}
handlers={[tooltip, callout]}
/>
)
}
import {
TooltipProvider,
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipArrow,
} from "@/components/ui/tooltip"
const tooltip: AnnotationHandler = {
name: "tooltip",
Inline: async ({ children, annotation }) => {
const { query, data } = annotation
const highlighted = await highlight(
{ value: query, lang: "ts", meta: "" },
"github-dark",
)
return (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger className="">
{children}
</TooltipTrigger>
<TooltipContent className="" sideOffset={0}>
<Pre code={highlighted} className="" />
<TooltipArrow className="" />
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
},
}
const callout: AnnotationHandler = {
name: "callout",
Block: ({ annotation, children }) => {
const { character, className } = annotation.data
return (
<>
{children}
<div
style={{ minWidth: `${character + 4}ch` }}
className={
"w-fit border bg-zinc-900 border-current rounded px-2 relative -ml-[1ch] mt-1 whitespace-break-spaces" +
" " +
className
}
>
<div
style={{ left: `${character + 1}ch` }}
className=""
/>
{annotation.query}
</div>
</>
)
},
}