slidev-magic-move について
SlidevでShiki Magic Moveを使ったコードプレゼンテーションを作るためのスキルです。チュートリアルやリファクタリングの説明、コード比較に向けたアニメーション設定を扱います。
主な機能
- 01コード状態間のアニメーション遷移
- 02追加・削除・移動したトークンの表示
- 03行ハイライトとの併用
- 04タイミングと行番号の設定
活用シーン
- 01コード変更の段階的な説明
- 02リファクタリングの実演
- 03変更前後のコード比較
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ yoanbernabeu/ slidev-skills --skill slidev-magic-moveインストール後、Claude Code で/slidev-magic-moveを入力して読み込まれたか確認します。
詳細
Shiki Magic Move
このスキルでは、Keynote のマジックムーブエフェクトのように、異なるコード状態間のスムーズなアニメーション付きトランジションを実現する強力な機能、Shiki Magic Move を扱います。
このスキルを使う場面
- コードの変化を段階的に見せる
- リファクタリングを実演する
- 変更前と変更後のコードを比較する
- チュートリアルで手順を説明する
- コードの変換をアニメーションで見せる
Magic Move の仕組み
Magic Move は 2 つのコードブロックを分析し、次の処理を行います。
- 変更されていないトークンを特定する
- 移動または名前変更されたトークンを見つける
- 追加または削除されたトークンを検出する
- トランジションをスムーズにアニメーション表示する
基本構文
md magic-move を指定して、4 つのバッククォートを使います。
````md magic-move
```js
console.log('Hello')
```
```js
console.log('Hello, World!')
```
````
各コードブロックが 1 つのステップを表し、クリックするとアニメーションが表示されます。
ステップごとの例
リファクタリングの流れ
````md magic-move
```js
// Step 1: Original code
function add(a, b) {
return a + b
}
```
```js
// Step 2: Add types
function add(a: number, b: number) {
return a + b
}
```
```js
// Step 3: Add return type
function add(a: number, b: number): number {
return a + b
}
```
```ts
// Step 4: Convert to arrow function
const add = (a: number, b: number): number => a + b
```
````
複数のステップ
````md magic-move
```js
const name = 'world'
```
```js
const name = 'world'
const greeting = 'Hello'
```
```js
const name = 'world'
const greeting = 'Hello'
console.log(`${greeting}, ${name}!`)
```
```js
const name = 'world'
const greeting = 'Hello'
console.log(`${greeting}, ${name}!`)
// Output: Hello, world!
```
````
行のハイライトとの組み合わせ
Magic Move 内でハイライトを追加します。
````md magic-move {lines: true}
```js {*|1|2|3}
const a = 1
const b = 2
const c = 3
```
```js {*}
const sum = 1 + 2 + 3
```
````
高度なオプション
アニメーションのタイミングを調整する
````md magic-move {at: 3}
```js
// Starts at click 3
const x = 1
```
```js
const x = 1
const y = 2
```
````
行番号を無効にする
````md magic-move {lines: false}
```js
const x = 1
```
```js
const x = 2
```
````
オプションの組み合わせ
````md magic-move {at: 2, lines: true}
```js {*|1|2}
const first = 1
const second = 2
```
```js {*}
const result = first + second
```
````
実例
React フックの変化
````md magic-move
```jsx
// Class component state
class Counter extends React.Component {
state = { count: 0 }
increment = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<button onClick={this.increment}>
{this.state.count}
</button>
)
}
}
```
```jsx
// Function component with useState
function Counter() {
const [count, setCount] = useState(0)
const increment = () => {
setCount(count + 1)
}
return (
<button onClick={increment}>
{count}
</button>
)
}
```
```jsx
// Simplified with inline handler
function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
)
}
```
````
API の変化
````md magic-move
```js
// Callbacks
function fetchUser(id, callback) {
fetch(`/api/users/${id}`)
.then(res => res.json())
.then(data => callback(null, data))
.catch(err => callback(err, null))
}
fetchUser(1, (err, user) => {
if (err) console.error(err)
else console.log(user)
})
```
```js
// Promises
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json())
}
fetchUser(1)
.then(user => console.log(user))
.catch(err => console.error(err))
```
```js
// Async/Await
async function fetchUser(id) {
const res = await fetch(`/api/users/${id}`)
return res.json()
}
try {
const user = await fetchUser(1)
console.log(user)
} catch (err) {
console.error(err)
}
```
````
関数を作成する
````md magic-move
```typescript
function processData() {
}
```
```typescript
function processData(data) {
}
```
```typescript
function processData(data: string[]) {
}
```
```typescript
function processData(data: string[]): string[] {
return data
}
```
```typescript
function processData(data: string[]): string[] {
return data
.filter(item => item.length > 0)
}
```
```typescript
function processData(data: string[]): string[] {
return data
.filter(item => item.length > 0)
.map(item => item.trim())
}
```
```typescript
function processData(data: string[]): string[] {
return data
.filter(item => item.length > 0)
.map(item => item.trim())
.sort((a, b) => a.localeCompare(b))
}
```
````
SQL クエリを作成する
````md magic-move
```sql
SELECT * FROM users
```
```sql
SELECT id, name, email FROM users
```
```sql
SELECT id, name, email FROM users
WHERE active = true
```
```sql
SELECT id, name, email FROM users
WHERE active = true
ORDER BY created_at DESC
```
```sql
SELECT id, name, email FROM users
WHERE active = true
ORDER BY created_at DESC
LIMIT 10
```
````
パターンとヒント
シンプルに始めて、複雑さを加える
````md magic-move
```js
// Start with the goal
const result = processData(input)
```
```js
// Show the implementation
function processData(input) {
return input
}
const result = processData(input)
```
```js
// Add details
function processData(input) {
return input
.filter(x => x != null)
.map(x => transform(x))
}
const result = processData(input)
```
````
問題を示してから解決策を示す
````md magic-move
```js
// Problem: Callback hell
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
// Finally done
})
})
})
})
```
```js
// Solution: Promises
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => getMoreData(c))
.then(d => {
// Clean and flat!
})
```
````
コメントで変更箇所を強調する
````md magic-move
```js
const config = {
debug: true,
timeout: 1000
}
```
```js
const config = {
debug: false, // Changed!
timeout: 1000
}
```
```js
const config = {
debug: false,
timeout: 5000 // Increased!
}
```
````
ベストプラクティス
- 小さなステップ:各トランジションでは、論理的な変更を 1 つ示します
- コンテキストを維持する:可能であれば、周囲のコードも表示したままにします
- コメントを使う:変更内容を説明するコメントを追加します
- 一貫したスタイル:すべてのステップで書式を統一します
- アニメーションをテストする:発表前にスムーズなトランジションを確認します
よくある間違い
❌ 一度に変更しすぎる
Step 1: Original code
Step 2: Completely different code
✓ 段階的に変更する
Step 1: Original
Step 2: Add one feature
Step 3: Add another feature
Step 4: Refactor
❌ コンテキストが失われる
Step 1: function foo() { ... }
Step 2: const bar = ... // Where did foo go?
✓ コンテキストを維持する
Step 1: function foo() { ... }
Step 2: function foo() { const bar = ... }
インタラクティブなプレイグラウンド
Magic Move をお試しください:https://shiki-magic-move.netlify.app/
出力形式
Magic Move アニメーションを作成する場合:
ANIMATION GOAL: [What transformation are you showing?]
STEPS:
1. [Initial state - describe]
2. [Change 1 - describe]
3. [Change 2 - describe]
...
CODE:
````md magic-move {[options]}
```[lang]
[Step 1 code]
[Step 2 code]
...
SPEAKER NOTES:
- Step 1: [What to say]
- Step 2: [What to say]
```
コメント
Markdown 対応