開発mdx-playground

やったこと

apps/web/app/pages/shogi-board-demo.vue に3つの機能を追加した。矢印キーで局面を進ませ、持ち駒を選んだら打てる候補マスに青枠を浮かばせ、文字で書いていた持ち駒をSVG駒画像に置き換えた。3つの機能を1ファイル内で順に積み上げ、HMRが拾うのを目で確認しながら進めた。

背景

棋譜を1手ずつ送るたびにマウスでボタンへ手を伸ばしていた。指がキーボードから離れると目線も切れる。さらに持ち駒を選択しても、どこに打てるかは盤面を眺めて推測するしかなかった。持ち駒の表示自体も「角」「飛」と文字で書いていて、盤上のSVG駒と並ぶと違和感が残った。3つの不満を1ファイルにまとめて潰す。

実装の流れ

1. 矢印キーで局面を進める

onMountedkeydown リスナーを登録し、onBeforeUnmount で解除する。prev() / next() に割り当てる。

const onKeydown = (e: KeyboardEvent) => {
  if (e.key === "ArrowRight") {
    e.preventDefault()
    next()
  } else if (e.key === "ArrowLeft") {
    e.preventDefault()
    prev()
  }
}

onMounted(() => window.addEventListener("keydown", onKeydown))
onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown))

preventDefault を入れないと、ページ全体が横スクロールに巻き込まれる場面があった。リスナー登録と解除のペアを忘れると、ページ遷移後もキーが効き続けてバグになる。

2. 持ち駒の打ち予告ハイライト

選択中の持ち駒を selectedHand で保持し、打てるマスを computed で算出する。盤面側のセルに is-droppable クラスを付けて青枠を出す。

const droppableSquares = computed(() => {
  if (!selectedHand.value) return new Set<string>()
  return computeDroppableSquares(board.value, selectedHand.value)
})

純粋関数 computeDroppableSquares に板状態と駒種を渡し、Setで返す。レンダリング側は droppableSquares.value.has(key) で即座に判定する。最初は配列で返していたが、マスごとに includes を回すと81マス分のループが走るため、Setに変えた。

3. 持ち駒をSVG駒に置き換え

持ち駒UIで <span>角</span> のように描いていた箇所を、盤上と同じSVGコンポーネントへ差し替えた。CSSがSVGに合わなかったので、width: 100%height: auto を当て、親の display: flex で中央寄せに直した。

<button class="hand-piece" :class="{ selected: selectedHand === piece }">
  <ShogiPieceSvg :piece="piece" />
  <span class="count">×{{ count }}</span>
</button>

文字幅基準で組んでいた CSS のままだとSVGが小さく潰れた。hand-piecemin-width を SVG のアスペクト比に合わせて広げ、選択中は outline: 2px solid #2563eb で青枠を浮かばせる。盤上のハイライトと色を揃えると、視線の往復が滑らかになる。

動作確認

Chrome DevTools MCP で localhost:3000/pages/shogi-board-demo を開き、HMRが反映されたタイミングで目視確認した。先手の持ち駒「角」をクリックすると青枠が乗り、盤上の打てるマスも同じ青で浮き上がった。 キーを押すと棋譜が1手進み、 で戻った。SVG駒は文字版より小さくなりすぎず、盤上の駒と同じ筆致で並んだ。

agent-browser ではなく Chrome DevTools MCP を選んだのは、ローカルの Vite dev サーバーに直接 Devtools プロトコルで繋いだ方がHMRイベントも観察できるため。

学びメモ

  • keydownpreventDefault を入れ忘れると、ページが横スクロールを始めて盤面が画面外へ滑った
  • droppableSquares を配列ではなくSetで返したら、81マスのレンダリング時間が指でわかるレベルで縮んだ
  • 文字とSVGを混在させていた持ち駒UIは、片方をSVGに揃えるだけで盤面との視線の引っかかりが消えた
  • 盤面の打ち予告と持ち駒の選択枠を同じ青色にしたら、「この駒をここに打つ」の対応関係が一目で繋がった

次にやること

  • 数字キー(1〜9)で段選択、a〜iで筋選択のショートカットを試す
  • 持ち駒が0枚のときは選択不可にする(現状は0枚でもクリックできてしまう)
  • 打ち予告ハイライトに二歩・打ち歩詰めの除外ロジックを足す