Excel講座60ファイル・110セクションをNuxt4に移植した一日の全記録
朝、CF精算表ワークシートの移植から手をつけた。Piniaストアの接続が通り、593件のテストが全て緑になった瞬間に「これなら講座本体もいける」と腹が決まり、そのままExcel講座60ファイル・110セクションの移行に突入した。気づけばTheaterViewerの改善、UIバグ5件の修正、WordPress CDN動画の生存確認まで手を広げていて、コミットログには29ファイル・8,622行の追加が刻まれていた。
1. CF精算表ワークシートの完全移植
Piniaストア導入と型定義の整備
旧プロジェクトのVuexベースのCF精算表(キャッシュ・フロー計算書ワークシート)を、Nuxt4のPiniaストアに載せ替えた。移植対象は以下の通り。
| 種別 | ファイル数 |
|---|---|
| Piniaストア定義 | 1 |
| 型定義・ユーティリティ | 数ファイル |
| Vueコンポーネント | 9 |
| テストファイル | 6(593テスト) |
暗黙的anyの型注釈が散在していたので、移植ついでにTypeScriptの型を全て明示した。「anyを潰す作業」は地味だが、ストアのgetterで返る型が不明なまま移行すると、コンポーネント側で型エラーが連鎖する。先に型を固めたことで、コンポーネント移植時のエラーがゼロになった。
593テスト全パス
テストは旧プロジェクトからそのまま持ってきて、import先をPiniaに書き換えた。pnpm test を叩いて593件が全て通ったときに、移植の正しさを確認できた。
2. Excel講座60ファイル・110セクションの大規模移行
Phase構成
移行は8フェーズに分けて進めた。
| Phase | 内容 |
|---|---|
| 0 | メディアアセット移行(GIF 14、PNG 19、Excel 23) |
| 1 | データ構造の定義 |
| 2 | セクションデータの変換 |
| 3 | コンポーネント接続 |
| 4 | ナビゲーション統合 |
| 5 | スタイル調整 |
| 6 | 動作確認 |
| 7 | 検証(TOC名とMillerChapter名の完全一致確認) |
サブエージェント並列でデータ変換
Phase 2のデータ変換では、サブエージェントを並列に走らせてセクションデータをTypeScript形式に一括変換した。60ファイルを手作業で変換していたら丸一日かかるところを、並列処理で一気に片付けた。
Phase 7の検証 — TOC名とMillerChapter名の突き合わせ
最終検証では、目次(TOC)のセクション名とMillerColumnレイアウトのチャプター名が完全に一致するかを突き合わせた。名前がずれていると、目次からクリックしたときに別のセクションが開くことになる。110セクション全てで一致を確認した。
3. TheaterViewer大幅改善
4つの機能を追加・移植した。
inlineTopicsモード
従来はトピックリストがサイドバーに分離していたが、アコーディオン式でコンテンツ列に統合するモードを追加した。画面幅が狭い環境でサイドバーが邪魔になる問題を解消。
stageHtml — 概要セクションのサマリーテーブル
概要セクションにサマリーテーブルを表示し、テーブル内の行をクリックすると該当セクションに遷移する機能。講座全体の俯瞰と個別セクションへの直接アクセスを両立させた。
SyntaxCard
Excel関数の構文をカード形式で表示するコンポーネント。関数名、引数、戻り値の型を構造化して見せる。
VideoSubtitlePlayer
字幕同期プレイヤーを旧プロジェクトから移植。動画の再生位置に合わせて字幕がハイライトされる。
4. UIバグ修正 — 5件の原因分析と対処
セクション戻りナビゲーションバグ
症状: 前のセクションに戻るボタンを押すと、そのセクションの最初のステップが開く。期待動作は「前のセクションの最後のステップ」に飛ぶこと。
原因分析: ナビゲーションは単純にセクションのURLに遷移していた。セクションを開いたときのデフォルト表示は最初のステップなので、「戻る」なのに先頭に飛んでしまう。
対処: URLに ?last=1 パラメータを付与する方式で解決した。セクションコンポーネント側で last=1 を検知したら最後のステップを初期表示にする。シンプルだがルーティングの変更なしで済む。
コンテンツ領域のmax-width制限
症状: コンテンツ領域の幅がmax-widthで制限されていて、ワイドモニターで左右に大きな余白が出る。
対処: TheaterViewerのコンテンツ領域からmax-width指定を解除。
テキスト-画像間の空白
症状: テキストブロックと画像ブロックの間に不要な空白が入る。
原因分析: flexコンテナ内の子要素が flex: 1 1 auto になっていて、余剰スペースを均等に吸収していた。テキスト量が少ない場合に、テキストブロックが伸びて空白が生まれる。
対処: flex: 0 1 auto に変更。子要素は自身のコンテンツサイズ以上に伸びなくなり、空白が消えた。
スクロール余白バグ
症状: ページ下部に大きな余白が残り、スクロールが長くなる。
原因分析: 非アクティブなステップの画像に opacity: 0 を適用していたが、要素自体はDOMに残っていてレイアウト上の領域を占有していた。見えないのにスペースだけ食っている状態。
対処: 非アクティブ画像に display: none または高さゼロを適用し、レイアウトから除外した。
プレースホルダー画像問題
症状: Excel演習のスクリーンショットが、全く関係ないセクションに表示されている。
原因調査: AIがセクションデータを生成した際に、手持ちの画像ファイルを「それっぽい」セクションに機械的に割り当てていたことが判明。教材の文脈を理解せずにファイル名だけで紐付けた結果、無関係な画像が混入していた。
対処: 問題のある画像をSVGプレースホルダーに置換した。旧プロジェクトのファイルと突き合わせ、正しい画像が特定できたセクションは差し替え、特定できないものはプレースホルダーのまま残した。
5. WordPress CDN動画の生存確認
curlの404誤検出
旧プロジェクトの動画はWordPressのCDNでホストされている。移行にあたって全URLの生存確認をcurlで行ったところ、日本語を含むURLが軒並み404を返した。
「CDNから消されたか」と一瞬焦ったが、curlに渡すURLの日本語部分がエンコードされていないことに気づいた。ブラウザは日本語URLを自動的にパーセントエンコードするが、curlはそのまま送る。手動でURLエンコードしてから再実行したところ、全て200 OKだった。
CSP設定更新
動画と画像がWordPress CDNから配信されるので、Content Security Policyの media-src と img-src にWordPress CDNのドメインを許可リストとして追加した。
6. 演習ページ35件の移行計画
講座には演習ページが35件ある。本体のセクション移行とは別に、演習ページ固有の構造を維持する必要がある。
計画で整理した移行対象の構造要素は以下の4つ。
- 上司/あなたのChatBoxコンポーネント(会話形式の指示表示)
- Excelファイルのダウンロードリンク
- ステップ番号付きの手順表示
- 埋め込み動画
演習ページの移行は翌日以降に持ち越し。
7. Codex使用制限の発覚と復旧
作業中、Codex CLIが「使用制限に達した」とエラーを返した。原因を調べたところ、ChatGPT Plusのサブスクリプションが期限切れになっていた。更新手続きを済ませたら即座に復活。Codexの利用がサブスクのステータスに紐づいていることを初めて知った。
今日の学び
curlで404が返ってきたとき、最初に「CDNのファイルが消えた」と疑った。しかし実際にはcurlが日本語URLをエンコードしていないだけだった。ブラウザで開けば正常に表示される。ツールの挙動の差異を疑う前に「CDNが壊れた」という結論に飛びつきかけた自分のバイアスに気づけたのは収穫だった。
UIバグ5件のうち、opacity: 0 の問題は見た目では全く気づけない。要素が透明なだけでレイアウト上は存在し続けている。DevToolsでボックスモデルを表示して初めて「ここに見えない要素が居座っている」と分かった。CSSの visibility: hidden と display: none の違いと同じ話だが、実際にスクロールが長くなる現象として体験すると、仕様の理解が体に染みる。