開発eurekapu-nuxt4メモ

Cockpit財務諸表インタラクティブコンテンツの移植とアニメーション全実装

旧プロジェクト(cockpit-nuxt-vuetify)の財務諸表インタラクティブコンテンツが、eurekapu-nuxt4へ完全移植できた。BS・PL・CSが連動して動き、仕訳をプッシュするたびに勘定科目の数値が緑/赤に色分けしながらカウントアップする。今日はその全工程を記録する。


08:52 セッション — 移植メイン実装

Codex 4ラウンドレビューで計画を固める

移植計画書をClaude Codeに書かせた後、Codex(GPT-5.5)に投げてレビューを回した。

ラウンド指摘数対応
1回目7件全件修正
2回目2件全件修正
3回目2件全件修正
4回目0件(致命的指摘なし)実装開始

3ラウンドで7→2→2→0と収束するのを見届けてから着手した。「とりあえず書いて後で直す」より、計画を固めてから手を動かした方が手戻りが少ない、というのが最近の実感だ。

Phase 0: extract.mjs で旧プロジェクトデータを自動収集

旧プロジェクトから取引データを手で写すのは間違いの温床になる。自動収集スクリプト(extract.mjs)を書いて、25取引データを変換した。

  • 27スナップショット出力
  • 最終時点: 現金期末 = CF純増減 = 300.9(整合差ゼロ)

整合差がゼロになった数字を画面で確認してから、次のフェーズに進んだ。

Phase 1: accounts-master.ts + transactions.ts を自動生成

スクリプトで生成した後、No23の所得税振替額が動的セットされていないバグを発見した。勘定科目が静的な初期値を返したまま、振替計算が走っていなかった。修正後に再度ベンチマークを確認して整合差ゼロを維持した。

Phase 2-5: コンポーネント群を新規作成

JournalExampleを流用せず、Cockpit専用で独立させた。コンポーネント構成は以下のとおり。

CockpitBs.vue         — 貸借対照表
CockpitPl.vue         — 損益計算書
CockpitCs.vue         — キャッシュフロー計算書(直接法 | 間接法)
CockpitJournalCard.vue     — 仕訳カード
CockpitJournalExample.vue  — 全体ラッパー
cockpitStore.ts       — Pinia store

書籍レイアウトに合わせてBS+PLを横並び、BSの下にCSを2列(直接法|間接法)で配置した。

飛びピルアニメーション

quiz/practiceと同等の実装にした。position: fixeddocument.body に挿入する方式だ。

// 起点座標を取得してbodyに差し込む
const rect = sourceEl.getBoundingClientRect()
const pill = document.createElement('div')
pill.style.cssText = `
  position: fixed;
  left: ${rect.left}px;
  top: ${rect.top}px;
  ...
`
document.body.appendChild(pill)
// CSSアニメーションで終点まで飛ばして remove

Teleportを使わない理由は、z-indexの管理がシンプルになるから。bodyに直接挿入すると、スタックコンテキストの問題が起きない。

AnimatedNumber コンポーネント

仕訳プッシュのたびに数値がカウントアップする。増加なら緑、減少なら赤に色が変わる。

<AnimatedNumber :value="account.balance" />

内部では requestAnimationFrame でフレームごとに値を更新している。60fpsで滑らかに動くが、変化量が大きい場合はdurationを伸ばして追従しやすくした。

事前行挿入: 論点移動時に勘定科目行を先に表示

取引を切り替えた瞬間、まだ残高がゼロの勘定科目でも行として表示しておく。仕訳をプッシュした時に「行が突然現れる」のを防ぐためだ。ユーザーが「この仕訳でこの勘定科目が動く」と予測できるようになる。


11:48 セッション — 後続改善

拡大モーダル廃止 → インライン拡大

Teleportを使ったモーダルを廃止して、.is-expanded クラスで全画面化するインライン方式に切り替えた。

.cockpit-wrapper.is-expanded {
  position: fixed;
  inset: 0;
  z-index: 1000;
  overflow-y: auto;
}

Teleportを外したことでDOMツリーがシンプルになり、アニメーション座標の計算がずれる問題も一緒に消えた。

矢印キーショートカット

で取引を移動できるようにした。ただしフォーム要素(input, select, textarea)にフォーカスが当たっている間は除外した。

const onKeydown = (e: KeyboardEvent) => {
  const tag = (e.target as HTMLElement).tagName
  if (['INPUT', 'SELECT', 'TEXTAREA'].includes(tag)) return
  if (e.key === 'ArrowRight') goNext()
  if (e.key === 'ArrowLeft') goPrev()
}

仕訳カードにCSカテゴリ名を表示

仕訳カードの2行目に「現金及び預金の増減(営業CF)」のようなラベルを追加した。どの取引がどのCFカテゴリに影響するかをカード単体で読めるようになった。

CSピルの起点と色を変更

CS(キャッシュフロー計算書)へのアニメーションピルを、これまでのBS/PL共用起点から cs-sub 行に移動した。色も紫(#7c3aed)に変えて、BS用・PL用ピルと視覚的に区別できるようにした。

SVG 22個を並列エージェントで移植

旧プロジェクトの cockpit-svgs コンポーネント22個をVue 3 + Pinia対応に変換した。並列エージェント7個で分担して一気に処理した。SVGは静的描画が多いが、一部のコンポーネントはストアの残高値を参照して棒グラフの高さを動的に変える実装になっている。

entry分割: 元本と利息を分離する

3取引(no-06 短期借入、no-14 長期借入、no-16 貸付)で、元本と利息を別エントリに分離した。

分割前分割後
借入金返済(元本+利息 一体)元本返済(財務CF)+ 利息支払(営業CF)

元本は財務CF、利息は営業CF——この違いをアニメーションで可視化するのが目的だ。ピルが2方向に飛ぶ動きで「同じ支払いでも行き先が違う」ことが視覚的にわかる。

ConnectorOverlay → 方針転換

BS・PL・CSをつなぐ矢印(ConnectorOverlay)を実装しようとした。DOM上の2点間に線を引く方式だ。

試してみると、スクロール・リサイズ・コンポーネント再描画のたびに座標がずれた。ResizeObserver で追いかけても、アニメーション中の座標は信頼できない。

方針を転換した。

「矢印より、アニメーション完了後にセルをZ軸浮上させる」

仕訳がプッシュされて数値が確定した後、対応するセルに box-shadow + transform: translateZ でフロート効果を出す。矢印で「どこがつながっているか」を示すより、セルが浮くことで「ここが今動いた」を伝える方が直感的だ。実装は次セッションへ持ち越した。

applyEntry の export 競合修正

cockpit-calc.tsapplyEntry が二重 export になっていて、TypeScriptのコンパイルエラーが出た。一方の export を除去して解消した。

excel/functions ページのHTML警告を並列修正

<table> 直下に <tr> を直接置いていたHTML構造警告が複数ページで出ていた。Agent並列で一括修正した。


今日のコミット

f209ff5 feat: Cockpit財務諸表コンポーネント移植・アニメーション全実装

引き継ぎドキュメントも作成して、次セッションでセルZ軸浮上を拾えるようにした。


振り返り

ConnectorOverlayは座標計算が3回書き直しても収束しなかった。「座標を追いかけるより、アニメーション完了後にセル自体を浮かせる」という方針転換は、実装量を減らしながら教育効果を上げる方向に動いた。行き詰まったら別の表現手段を探す、という切り替えがうまく機能した日だった。