Smoothly animated code blocks with Shiki. Online Demo.
This is a rather low-level library, you usually want to use it with a high-level integrations like Slidev.
The package provides framework-agnostic core and renderer and framework wrappers for Vue and React.
Each of the framework wrappers provides the following components:
ShikiMagicMove
- the main component to wrap the code blockShikiMagicMovePrecompiled
- animations for compiled tokens, without the dependency on ShikiShikiMagicMoveRenderer
- the low-level renderer component
ShikiMagicMove
requires you to provide a Shiki highlighter instance. For example, in Vue:
<script setup>
import { ShikiMagicMove } from 'shiki-magic-move/vue'
import { getHighlighter } from 'shiki'
import { ref } from 'vue'
const highlighter = await getHighlighter({
theme: 'nord',
langs: ['javascript', 'typescript'],
})
const code = ref(`const hello = 'world'`)
function animate() {
code.value = `let hi = 'hello'`
}
</script>
<template>
<ShikiMagicMove :highlighter="highlighter" lang="ts" :code="code" />
<button @click="animate">
Animate
</button>
</template>
Whenever the code
changes, the component will animate the changes.
ShikiMagicMovePrecompiled
is a lighter version of ShikiMagicMove
that doesn't require Shiki. It's useful when you want to animate the compiled tokens directly. For example, in Vue:
<script setup>
import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue'
import { ref } from 'vue'
const step = ref(1)
const compiledSteps = [/* Compiled token steps */]
</script>
<template>
<ShikiMagicMovePrecompiled
:steps="compiledSteps"
:step="step"
/>
<button @click="step++">
Next
</button>
</template>
To get the compiled tokens, you can run this somewhere else and serialize them into the component:
import { codeToKeyedTokens, createMagicMoveMachine } from 'shiki-magic-move/core'
import { getHighlighter } from 'shiki'
const shiki = await getHighlighter({
theme: 'nord',
langs: ['javascript', 'typescript'],
})
const codeSteps = [
`const hello = 'world'`,
`let hi = 'hello'`,
]
const machine = createMagicMoveMachine(
code => codeToKeyedTokens(shiki, code, {
lang: 'ts',
theme: 'nord',
}),
{
// options
}
)
const compiledSteps = codeSteps.map(code => machine.commit(code).current)
// Pass `compiledSteps` to the precompiled component
// If you do this on server-side or build-time, you can serialize `compiledSteps` into JSON
Check this blog post.
MIT License © 2023-PRESENT Anthony Fu