[{"data":1,"prerenderedAt":507},["ShallowReactive",2],{"content-/eurekapu-excel-course-migration":3,"all-pages-for-dir":505,"og-image-/eurekapu-excel-course-migration":506},{"id":4,"title":5,"body":6,"category":484,"description":485,"extension":486,"meta":487,"navigation":488,"path":489,"project_name":490,"published":491,"publishedAt":492,"seo":493,"stem":494,"tags":495,"todo":502,"updatedAt":503,"__hash__":504},"pages/2026-04/2026-04-08/eurekapu-excel-course-migration.md","Excel講座60ファイル・110セクションをNuxt4に移植した一日の全記録",{"type":7,"value":8,"toc":450},"minimark",[9,13,17,20,25,30,33,85,88,92,100,102,106,110,113,190,193,196,200,203,205,209,212,216,219,223,226,230,233,237,240,242,246,249,256,262,276,279,284,289,292,297,306,315,318,323,332,341,344,349,355,360,362,366,369,372,375,379,390,392,396,399,402,418,421,423,427,430,432,435,438],[10,11,5],"h1",{"id":12},"excel講座60ファイル110セクションをnuxt4に移植した一日の全記録",[14,15,16],"p",{},"朝、CF精算表ワークシートの移植から手をつけた。Piniaストアの接続が通り、593件のテストが全て緑になった瞬間に「これなら講座本体もいける」と腹が決まり、そのままExcel講座60ファイル・110セクションの移行に突入した。気づけばTheaterViewerの改善、UIバグ5件の修正、WordPress CDN動画の生存確認まで手を広げていて、コミットログには29ファイル・8,622行の追加が刻まれていた。",[18,19],"hr",{},[21,22,24],"h2",{"id":23},"_1-cf精算表ワークシートの完全移植","1. CF精算表ワークシートの完全移植",[26,27,29],"h3",{"id":28},"piniaストア導入と型定義の整備","Piniaストア導入と型定義の整備",[14,31,32],{},"旧プロジェクトのVuexベースのCF精算表（キャッシュ・フロー計算書ワークシート）を、Nuxt4のPiniaストアに載せ替えた。移植対象は以下の通り。",[34,35,36,49],"table",{},[37,38,39],"thead",{},[40,41,42,46],"tr",{},[43,44,45],"th",{},"種別",[43,47,48],{},"ファイル数",[50,51,52,61,69,77],"tbody",{},[40,53,54,58],{},[55,56,57],"td",{},"Piniaストア定義",[55,59,60],{},"1",[40,62,63,66],{},[55,64,65],{},"型定義・ユーティリティ",[55,67,68],{},"数ファイル",[40,70,71,74],{},[55,72,73],{},"Vueコンポーネント",[55,75,76],{},"9",[40,78,79,82],{},[55,80,81],{},"テストファイル",[55,83,84],{},"6（593テスト）",[14,86,87],{},"暗黙的anyの型注釈が散在していたので、移植ついでにTypeScriptの型を全て明示した。「anyを潰す作業」は地味だが、ストアのgetterで返る型が不明なまま移行すると、コンポーネント側で型エラーが連鎖する。先に型を固めたことで、コンポーネント移植時のエラーがゼロになった。",[26,89,91],{"id":90},"_593テスト全パス","593テスト全パス",[14,93,94,95,99],{},"テストは旧プロジェクトからそのまま持ってきて、import先をPiniaに書き換えた。",[96,97,98],"code",{},"pnpm test"," を叩いて593件が全て通ったときに、移植の正しさを確認できた。",[18,101],{},[21,103,105],{"id":104},"_2-excel講座60ファイル110セクションの大規模移行","2. Excel講座60ファイル・110セクションの大規模移行",[26,107,109],{"id":108},"phase構成","Phase構成",[14,111,112],{},"移行は8フェーズに分けて進めた。",[34,114,115,125],{},[37,116,117],{},[40,118,119,122],{},[43,120,121],{},"Phase",[43,123,124],{},"内容",[50,126,127,135,142,150,158,166,174,182],{},[40,128,129,132],{},[55,130,131],{},"0",[55,133,134],{},"メディアアセット移行（GIF 14、PNG 19、Excel 23）",[40,136,137,139],{},[55,138,60],{},[55,140,141],{},"データ構造の定義",[40,143,144,147],{},[55,145,146],{},"2",[55,148,149],{},"セクションデータの変換",[40,151,152,155],{},[55,153,154],{},"3",[55,156,157],{},"コンポーネント接続",[40,159,160,163],{},[55,161,162],{},"4",[55,164,165],{},"ナビゲーション統合",[40,167,168,171],{},[55,169,170],{},"5",[55,172,173],{},"スタイル調整",[40,175,176,179],{},[55,177,178],{},"6",[55,180,181],{},"動作確認",[40,183,184,187],{},[55,185,186],{},"7",[55,188,189],{},"検証（TOC名とMillerChapter名の完全一致確認）",[26,191,192],{"id":192},"サブエージェント並列でデータ変換",[14,194,195],{},"Phase 2のデータ変換では、サブエージェントを並列に走らせてセクションデータをTypeScript形式に一括変換した。60ファイルを手作業で変換していたら丸一日かかるところを、並列処理で一気に片付けた。",[26,197,199],{"id":198},"phase-7の検証-toc名とmillerchapter名の突き合わせ","Phase 7の検証 — TOC名とMillerChapter名の突き合わせ",[14,201,202],{},"最終検証では、目次（TOC）のセクション名とMillerColumnレイアウトのチャプター名が完全に一致するかを突き合わせた。名前がずれていると、目次からクリックしたときに別のセクションが開くことになる。110セクション全てで一致を確認した。",[18,204],{},[21,206,208],{"id":207},"_3-theaterviewer大幅改善","3. TheaterViewer大幅改善",[14,210,211],{},"4つの機能を追加・移植した。",[26,213,215],{"id":214},"inlinetopicsモード","inlineTopicsモード",[14,217,218],{},"従来はトピックリストがサイドバーに分離していたが、アコーディオン式でコンテンツ列に統合するモードを追加した。画面幅が狭い環境でサイドバーが邪魔になる問題を解消。",[26,220,222],{"id":221},"stagehtml-概要セクションのサマリーテーブル","stageHtml — 概要セクションのサマリーテーブル",[14,224,225],{},"概要セクションにサマリーテーブルを表示し、テーブル内の行をクリックすると該当セクションに遷移する機能。講座全体の俯瞰と個別セクションへの直接アクセスを両立させた。",[26,227,229],{"id":228},"syntaxcard","SyntaxCard",[14,231,232],{},"Excel関数の構文をカード形式で表示するコンポーネント。関数名、引数、戻り値の型を構造化して見せる。",[26,234,236],{"id":235},"videosubtitleplayer","VideoSubtitlePlayer",[14,238,239],{},"字幕同期プレイヤーを旧プロジェクトから移植。動画の再生位置に合わせて字幕がハイライトされる。",[18,241],{},[21,243,245],{"id":244},"_4-uiバグ修正-5件の原因分析と対処","4. UIバグ修正 — 5件の原因分析と対処",[26,247,248],{"id":248},"セクション戻りナビゲーションバグ",[14,250,251,255],{},[252,253,254],"strong",{},"症状",": 前のセクションに戻るボタンを押すと、そのセクションの最初のステップが開く。期待動作は「前のセクションの最後のステップ」に飛ぶこと。",[14,257,258,261],{},[252,259,260],{},"原因分析",": ナビゲーションは単純にセクションのURLに遷移していた。セクションを開いたときのデフォルト表示は最初のステップなので、「戻る」なのに先頭に飛んでしまう。",[14,263,264,267,268,271,272,275],{},[252,265,266],{},"対処",": URLに ",[96,269,270],{},"?last=1"," パラメータを付与する方式で解決した。セクションコンポーネント側で ",[96,273,274],{},"last=1"," を検知したら最後のステップを初期表示にする。シンプルだがルーティングの変更なしで済む。",[26,277,278],{"id":278},"コンテンツ領域のmax-width制限",[14,280,281,283],{},[252,282,254],{},": コンテンツ領域の幅がmax-widthで制限されていて、ワイドモニターで左右に大きな余白が出る。",[14,285,286,288],{},[252,287,266],{},": TheaterViewerのコンテンツ領域からmax-width指定を解除。",[26,290,291],{"id":291},"テキスト-画像間の空白",[14,293,294,296],{},[252,295,254],{},": テキストブロックと画像ブロックの間に不要な空白が入る。",[14,298,299,301,302,305],{},[252,300,260],{},": flexコンテナ内の子要素が ",[96,303,304],{},"flex: 1 1 auto"," になっていて、余剰スペースを均等に吸収していた。テキスト量が少ない場合に、テキストブロックが伸びて空白が生まれる。",[14,307,308,310,311,314],{},[252,309,266],{},": ",[96,312,313],{},"flex: 0 1 auto"," に変更。子要素は自身のコンテンツサイズ以上に伸びなくなり、空白が消えた。",[26,316,317],{"id":317},"スクロール余白バグ",[14,319,320,322],{},[252,321,254],{},": ページ下部に大きな余白が残り、スクロールが長くなる。",[14,324,325,327,328,331],{},[252,326,260],{},": 非アクティブなステップの画像に ",[96,329,330],{},"opacity: 0"," を適用していたが、要素自体はDOMに残っていてレイアウト上の領域を占有していた。見えないのにスペースだけ食っている状態。",[14,333,334,336,337,340],{},[252,335,266],{},": 非アクティブ画像に ",[96,338,339],{},"display: none"," または高さゼロを適用し、レイアウトから除外した。",[26,342,343],{"id":343},"プレースホルダー画像問題",[14,345,346,348],{},[252,347,254],{},": Excel演習のスクリーンショットが、全く関係ないセクションに表示されている。",[14,350,351,354],{},[252,352,353],{},"原因調査",": AIがセクションデータを生成した際に、手持ちの画像ファイルを「それっぽい」セクションに機械的に割り当てていたことが判明。教材の文脈を理解せずにファイル名だけで紐付けた結果、無関係な画像が混入していた。",[14,356,357,359],{},[252,358,266],{},": 問題のある画像をSVGプレースホルダーに置換した。旧プロジェクトのファイルと突き合わせ、正しい画像が特定できたセクションは差し替え、特定できないものはプレースホルダーのまま残した。",[18,361],{},[21,363,365],{"id":364},"_5-wordpress-cdn動画の生存確認","5. WordPress CDN動画の生存確認",[26,367,368],{"id":368},"curlの404誤検出",[14,370,371],{},"旧プロジェクトの動画はWordPressのCDNでホストされている。移行にあたって全URLの生存確認をcurlで行ったところ、日本語を含むURLが軒並み404を返した。",[14,373,374],{},"「CDNから消されたか」と一瞬焦ったが、curlに渡すURLの日本語部分がエンコードされていないことに気づいた。ブラウザは日本語URLを自動的にパーセントエンコードするが、curlはそのまま送る。手動でURLエンコードしてから再実行したところ、全て200 OKだった。",[26,376,378],{"id":377},"csp設定更新","CSP設定更新",[14,380,381,382,385,386,389],{},"動画と画像がWordPress CDNから配信されるので、Content Security Policyの ",[96,383,384],{},"media-src"," と ",[96,387,388],{},"img-src"," にWordPress CDNのドメインを許可リストとして追加した。",[18,391],{},[21,393,395],{"id":394},"_6-演習ページ35件の移行計画","6. 演習ページ35件の移行計画",[14,397,398],{},"講座には演習ページが35件ある。本体のセクション移行とは別に、演習ページ固有の構造を維持する必要がある。",[14,400,401],{},"計画で整理した移行対象の構造要素は以下の4つ。",[403,404,405,409,412,415],"ul",{},[406,407,408],"li",{},"上司/あなたのChatBoxコンポーネント（会話形式の指示表示）",[406,410,411],{},"Excelファイルのダウンロードリンク",[406,413,414],{},"ステップ番号付きの手順表示",[406,416,417],{},"埋め込み動画",[14,419,420],{},"演習ページの移行は翌日以降に持ち越し。",[18,422],{},[21,424,426],{"id":425},"_7-codex使用制限の発覚と復旧","7. Codex使用制限の発覚と復旧",[14,428,429],{},"作業中、Codex CLIが「使用制限に達した」とエラーを返した。原因を調べたところ、ChatGPT Plusのサブスクリプションが期限切れになっていた。更新手続きを済ませたら即座に復活。Codexの利用がサブスクのステータスに紐づいていることを初めて知った。",[18,431],{},[21,433,434],{"id":434},"今日の学び",[14,436,437],{},"curlで404が返ってきたとき、最初に「CDNのファイルが消えた」と疑った。しかし実際にはcurlが日本語URLをエンコードしていないだけだった。ブラウザで開けば正常に表示される。ツールの挙動の差異を疑う前に「CDNが壊れた」という結論に飛びつきかけた自分のバイアスに気づけたのは収穫だった。",[14,439,440,441,443,444,385,447,449],{},"UIバグ5件のうち、",[96,442,330],{}," の問題は見た目では全く気づけない。要素が透明なだけでレイアウト上は存在し続けている。DevToolsでボックスモデルを表示して初めて「ここに見えない要素が居座っている」と分かった。CSSの ",[96,445,446],{},"visibility: hidden",[96,448,339],{}," の違いと同じ話だが、実際にスクロールが長くなる現象として体験すると、仕様の理解が体に染みる。",{"title":451,"searchDepth":452,"depth":452,"links":453},"",2,[454,459,464,470,477,481,482,483],{"id":23,"depth":452,"text":24,"children":455},[456,458],{"id":28,"depth":457,"text":29},3,{"id":90,"depth":457,"text":91},{"id":104,"depth":452,"text":105,"children":460},[461,462,463],{"id":108,"depth":457,"text":109},{"id":192,"depth":457,"text":192},{"id":198,"depth":457,"text":199},{"id":207,"depth":452,"text":208,"children":465},[466,467,468,469],{"id":214,"depth":457,"text":215},{"id":221,"depth":457,"text":222},{"id":228,"depth":457,"text":229},{"id":235,"depth":457,"text":236},{"id":244,"depth":452,"text":245,"children":471},[472,473,474,475,476],{"id":248,"depth":457,"text":248},{"id":278,"depth":457,"text":278},{"id":291,"depth":457,"text":291},{"id":317,"depth":457,"text":317},{"id":343,"depth":457,"text":343},{"id":364,"depth":452,"text":365,"children":478},[479,480],{"id":368,"depth":457,"text":368},{"id":377,"depth":457,"text":378},{"id":394,"depth":452,"text":395},{"id":425,"depth":452,"text":426},{"id":434,"depth":452,"text":434},"dev","CF精算表ワークシートの完全移植（593テスト全パス）、Excel講座60ファイル・110セクションの大規模移行、TheaterViewer大幅改善、UIバグ5件修正、WordPress CDN動画の生存確認まで、eurekapu-nuxt4プロジェクトの大規模移行作業の記録","md",{},true,"/eurekapu-excel-course-migration","eurekapu-nuxt4",false,"2026-04-08T00:00:00.000Z",{"title":5,"description":485},"2026-04/2026-04-08/eurekapu-excel-course-migration",[490,496,497,498,499,500,501],"Excel講座","Nuxt4移行","TheaterViewer","UIバグ修正","Pinia","CSP","memo",null,"rJgAaGqCF5F5CYzfGfO91BpnRdoUixNG9yI4XqsRM2Q",[],"https://log.eurekapu.com/favicon.svg",1775680421411]