[go: up one dir, main page]

Skip to content

shikijs/shiki-magic-move

Repository files navigation

shiki-magic-move

npm version npm downloads bundle JSDocs License

Smoothly animated code blocks with Shiki. Online Demo.

Usage

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 block
  • ShikiMagicMovePrecompiled - animations for compiled tokens, without the dependency on Shiki
  • ShikiMagicMoveRenderer - the low-level renderer component

ShikiMagicMove

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

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

How it works

Check this blog post.

Sponsors

License

MIT License © 2023-PRESENT Anthony Fu