Code

Animating code

The <Code> component uses Shiki for beautiful syntax highlighting:

     
<script lang="ts">
	import { Presentation, Slide, Code, Action } from '@animotion/core'

	let code: ReturnType<typeof Code>
</script>

<Presentation>
	<Slide class="h-full place-content-center place-items-center">
		<Code
			bind:this={code}
			lang="svelte"
			theme="poimandres"
			code={`
				<script>
					let count = 0
					$: double = count * 2
				<\/script>

				<button on:click={() => count++}>
					{double}
				</button>
			`}
			options={{ duration: 1000, stagger: 0.3, lineNumbers: true, containerStyle: false }}
		/>

		<Action
			do={() =>
				code.update`
					<script>
						let count = $state(0)
						let double = $derived(count * 2)
					<\/script>

					<button onclick={() => count++}>
						{double}
					</button>
				`}
		/>

		<Action do={() => code.selectLines`2,3`} />
		<Action do={() => code.selectLines`2-3,7`} />
		<Action do={() => code.selectToken`double {double}`} />
		<Action do={() => code.selectLines`*`} />
	</Slide>
</Presentation>

You can pick one of many themes that come with Shiki, choose a language, and provide options to the <Code /> component.

Animating Code Changes

You can animate changes in your code by using the update method:

code.update`
	<script>
		let count = $state(0)
		let double = $derived(count * 2)
	<\/script>

	<button onclick={() => count++}>
		{double}
	</button>
`

Animotion uses Shiki Magic Move to animate the code changes which does the diffing to know what changed, and then animates the changes.

Code Selection

You can select lines of code:

// select line
code.selectLines`2`
// select multiple lines
code.selectLines`2,3`
// select lines range
code.selectLines`2-3`
// select lines range and lines
code.selectLines`2-3,7`
// select all lines
code.selectLines`*`

You can select parts of the code:

// select every token
code.select`count`
// select first match
code.select`count:0`
// select first match on line 4
code.select`4 count:0`
// add another selection
code.selectAdd`() => count++`

The methods return a promise that is resolved when the transition is done:

<Action do={async () => {
	await code.selectLines`2,3`
	await code.selectLines`2-3,7`
	await code.select`count`
	await code.selectLines`*`
}} />

Code Scrolling

You might have a scrollable code block:

<Code
	bind:this={code}
	lang="svelte"
	theme="poimandres"
	code={`...`}
	class="h-[400px] overflow-y-auto"
	options={{ duration: 600, stagger: 0.3, containerStyle: false }}
/>

You can scroll to a line of code using the scrollToLine method:

await code.scrollToLine`2`

You can hide the scrollbar using CSS:

pre::-webkit-scrollbar {
	display: none;
}

You can also create a Tailwind class:

@utility no-scrollbar {
  @apply [scrollbar-width:none] [&::-webkit-scrollbar]:hidden;
}

Using Expressions

The update, selectLines, selectTokens, and scrollToLine tag functions support expressions:

<script lang="ts">
	let expression = 'false'
</script>

<!-- ... -->

<Action
	do={() => {
		expression = 'true'
		code.update`let bool = ${expression}`		
	}}
>

Code Indentation

Indenting code creates extra whitespace:

<Code
	code={`
->-><script>
->->->let count = 0
->->->$: double = count * 2
->-><\/script>

->-><button on:click={() => count++}>
->->->{double}
->-></button>
	`}
/>

If you use tabs Animotion auto-indents your code for you:

<Code
	code={`
		<script>
		->let count = 0
		->$: double = count * 2
		<\/script>
		<button on:click={() => count++}>
		->{double}
		</button>
	`}
/>

If you want to opt-out of this feature, you can set autoIndent to false:

<Code	autoIndent={false} />

Chaining code animations

Instead of creating actions for the code animations yourself, you can use the codes prop to create them for you:

<script lang="ts">
	import { Code } from '@animotion/core'

	// get a reference to the instance
	let code: ReturnType<typeof Code>
</script>

<Code
	ref={(ref) => code = ref}
	lang="svelte"
	theme="poimandres"
	codes={[
		`
			<script>
				let count = 0
				$: double = count * 2
			<\/script>

			<button on:click={() => count++}>
				{double}
			</button>
		`,
		`
			<script>
				let count = $state(0)
				let double = $derived(count * 2)
			<\/script>

			<button onclick={() => count++}>
				{double}
			</button>
		`
	]}
/>

<Action
	undo={() => {
		code.selectLines`*`
	}}
	actions={[
		() => code.selectLines`2,3`,
		() => code.selectLines`2-3,7`,
		() => code.selectToken`double {double}`,
		() => code.selectLines`*`
	]}
/>

Escape closing tags

Having a closing tag like </script> in your code block is going to cause problems because Svelte thinks you’re trying to close the <script> tag in your component. To solve this problem use the backslash character to escape it:

<Code>
  code={`
    <script>
      // ...
    <\/script>
  `}
<Code>