Home

2026-01-25 nextjs-migration plan

Next.js移行 実装計画

概要

Gemini Live API実装に向けたNext.js移行(A案)の実装計画。既存のweb/index.html(バニラJS、約1005行)をNext.js 15 + React 19 + TypeScriptに完全移植する。

選択した方針: A案

  • 先にNext.js化してからGemini Live実装
  • 既存web/index.htmlの全機能を移植
  • TypeScriptの型安全性でオーディオ処理やWebSocket管理のバグを減らす

移植対象機能

  • SSEストリーミングによるコマンド実行(plan, fullstack, go, nextjs, discuss, research)
  • イベント表示(tool_use, text, thinking等)
  • ファイルセレクター(5フォルダ対応)
  • 質問UI(単一選択/複数選択、カスタム入力)
  • 計画承認UI
  • セッション継続機能
  • コスト表示

懸念点と解決策

解決済み

懸念点解決策
SSEバッファ処理TextDecoderのstream: trueオプション使用、不完全行のバッファリング(詳細は後述)
セッション状態管理page.tsxで一元管理し、propsで子コンポーネントに伝播(状態フロー図参照)
PlanApproval表示条件出力テキストの文字列検出ロジック(詳細は後述)

要確認(実装前にユーザー確認が必要)

懸念点選択肢
バックエンドURL開発時: localhost:8080、本番: 環境変数 NEXT_PUBLIC_API_URL で設定
認証現在と同様、認証なしで進める
web/index.htmlNext.js完成後は削除(A案の方針)

修正範囲の全体像

Loading diagram...

状態管理フロー

Loading diagram...

状態の責務

状態管理場所更新タイミング
currentSessionIdpage.tsxSSEイベント init 受信時
totalCostpage.tsxSSEイベント complete 受信時
eventspage.tsx各SSEイベント受信時
isLoadingpage.tsxコマンド実行開始/完了時
questionspage.tsxSSEイベント question 受信時
resultpage.tsxSSEイベント complete 受信時
showApprovalpage.tsxresult内容の文字列判定で決定
projectPathuseSessionManagementlocalStorageから復元/保存

SSEバッファ処理の詳細設計

処理フロー

  1. ReadableStreamからチャンクを読み取る
  2. TextDecoderでstream: trueオプションを使用してデコード(マルチバイト文字対応)
  3. 改行で分割し、最後の不完全行はバッファに保持
  4. data: プレフィックスを持つ行のみJSONパースしてイベント発火

重要ポイント

  • stream: trueオプション: マルチバイト文字(日本語等)が途中で切れた場合に正しく処理
  • 不完全行の保持: 改行で終わっていない最後の行をバッファに戻す
  • data: プレフィックス: SSEフォーマットに従い、このプレフィックスがない行は無視

PlanApproval表示条件

判定ロジック

resultのoutputに以下のキーワードが含まれる場合にPlanApprovalを表示:

  • 「承認をお待ち」
  • 「waiting for approval」
  • 「Ready for approval」

表示フロー

Loading diagram...

ファイルパス結合ロジック

CommandFormの仕様

選択ファイルと引数入力を結合してargsを生成:

ファイル選択引数入力結果のargs
選択あり入力ありファイルパス + 空白 + 引数
選択あり入力なしファイルパスのみ
選択なし入力あり引数のみ
選択なし入力なし空文字

質問回答のmultiSelect対応

QuestionSectionの仕様

単一選択(multiSelect: false)

  • 選択肢クリックで即座に回答送信
  • 選択後はUIを非表示

複数選択(multiSelect: true)

  • 選択肢クリックで選択状態をトグル
  • 「送信」ボタン押下で選択肢をカンマ区切りで結合して送信

カスタム入力

  • テキスト入力フィールド + 「送信」ボタン
  • 入力内容をそのまま送信

変更ファイル一覧

ファイル変更内容影響度
frontend/package.jsonプロジェクト設定(Next.js 15, React 19, TypeScript)
frontend/tsconfig.jsonTypeScript設定
frontend/tailwind.config.tsTailwind CSS設定
frontend/next.config.tsNext.js設定(APIプロキシ、環境変数)
frontend/src/app/layout.tsxルートレイアウト
frontend/src/app/page.tsxメインページ(状態管理の中心)
frontend/src/types/index.ts型定義(StreamEvent, Question等)
frontend/src/hooks/useSSEStream.tsSSEストリーム処理フック
frontend/src/hooks/useSessionManagement.tsセッション・コスト管理フック
frontend/src/hooks/useFileSelector.tsファイル選択フック
frontend/src/components/CommandForm.tsxフォームセクション
frontend/src/components/ProgressContainer.tsxプログレス表示コンテナ
frontend/src/components/EventList.tsxイベントリスト表示
frontend/src/components/EventItem.tsx個別イベント表示(展開可能)
frontend/src/components/QuestionSection.tsx質問UI(multiSelect対応)
frontend/src/components/PlanApproval.tsx計画承認UI
frontend/src/components/LoadingIndicator.tsxローディング表示
frontend/src/lib/api.tsAPI呼び出しユーティリティ
frontend/src/lib/constants.ts定数(ALL_DEV_FOLDERS等)

実装ステップ

Phase 1: プロジェクト基盤構築

Step 1: Next.jsプロジェクト初期化

  • create-next-app@latestfrontend/ ディレクトリを作成
  • App Router、TypeScript、Tailwind CSS、ESLint を有効化
  • src/ ディレクトリ使用
  • 不要な初期ファイル(page.tsx のデフォルト内容等)を削除

Step 2: 型定義の作成

  • frontend/src/types/index.ts を作成
  • バックエンドの internal/service/types.go と整合性を取る
  • StreamEvent, Question, Option, CommandRequest, FileInfo等

Step 3: 定数ファイルの作成

  • frontend/src/lib/constants.ts を作成
  • ALL_DEV_FOLDERS 配列(バックエンドと同期)
  • 許可コマンドリスト

Step 4: Next.js設定(APIプロキシ)

  • frontend/next.config.ts を編集
  • /api/* をバックエンドにプロキシ
  • 環境変数 NEXT_PUBLIC_API_URL のサポート

Phase 2: カスタムフック実装

Step 5: useSSEStreamフック

  • frontend/src/hooks/useSSEStream.ts
  • fetch + ReadableStream でSSE処理
  • バッファ処理(前述の詳細設計に従う)
  • エラーハンドリング

Step 6: useSessionManagementフック

  • frontend/src/hooks/useSessionManagement.ts
  • localStorageでプロジェクトパスを永続化
  • セッションIDとコストは page.tsx で管理(このフックは localStorage 操作のみ)

Step 7: useFileSelectorフック

  • frontend/src/hooks/useFileSelector.ts
  • GET /api/files を呼び出し
  • フォルダ別ファイル一覧を取得

Phase 3: コンポーネント実装

Step 8: LoadingIndicatorコンポーネント

  • frontend/src/components/LoadingIndicator.tsx
  • Tailwind CSS でスピナーアニメーション
  • シンプルな表示のみ

Step 9: EventItemコンポーネント

  • frontend/src/components/EventItem.tsx
  • ツール別の表示フォーマット(Read, Write, Edit, Bash等)
  • 展開可能テキスト(200文字以上で折りたたみ)

Step 10: EventListコンポーネント

  • frontend/src/components/EventList.tsx
  • EventItem の配列をレンダリング
  • 自動スクロール

Step 11: PlanApprovalコンポーネント

  • frontend/src/components/PlanApproval.tsx
  • 承認/拒否ボタン
  • onApprove, onReject コールバック

Step 12: QuestionSectionコンポーネント

  • frontend/src/components/QuestionSection.tsx
  • 単一選択/複数選択対応
  • カスタム入力フィールド

Step 13: ProgressContainerコンポーネント

  • frontend/src/components/ProgressContainer.tsx
  • EventList, QuestionSection, PlanApproval を統括
  • 表示/非表示の制御

Step 14: CommandFormコンポーネント

  • frontend/src/components/CommandForm.tsx
  • プロジェクトパス入力
  • コマンド選択ドロップダウン
  • ファイルセレクター
  • 引数入力
  • 実行ボタン

Phase 4: ページ統合

Step 15: メインページ実装

  • frontend/src/app/page.tsx
  • 状態管理の中心
  • CommandForm と ProgressContainer を統合
  • useSSEStream との連携

Phase 5: 動作確認

Step 16: ローカル動作確認

  • バックエンドを起動(go run ./cmd/server
  • フロントエンドを起動(npm run dev
  • 全6コマンドの動作確認
  • ファイルセレクターの動作確認
  • 質問応答の動作確認
  • セッション継続の動作確認

ディレクトリ構造(完成形)

frontend/
├── src/
│   ├── app/
│   │   ├── layout.tsx          # ルートレイアウト
│   │   └── page.tsx            # メインページ(状態管理中心)
│   ├── components/
│   │   ├── CommandForm.tsx     # フォームセクション
│   │   ├── ProgressContainer.tsx # プログレス表示
│   │   ├── EventList.tsx       # イベントリスト
│   │   ├── EventItem.tsx       # 個別イベント(展開可能)
│   │   ├── QuestionSection.tsx # 質問UI
│   │   ├── PlanApproval.tsx    # 計画承認
│   │   └── LoadingIndicator.tsx # ローディング
│   ├── hooks/
│   │   ├── useSSEStream.ts     # SSE処理
│   │   ├── useSessionManagement.ts # localStorage操作
│   │   └── useFileSelector.ts  # ファイル選択
│   ├── lib/
│   │   ├── api.ts              # API呼び出し
│   │   └── constants.ts        # 定数定義
│   └── types/
│       └── index.ts            # 型定義
├── next.config.ts              # Next.js設定
├── tailwind.config.ts          # Tailwind設定
├── tsconfig.json               # TypeScript設定
└── package.json                # 依存関係

設計判断

判断選択した方法理由
SSE処理fetch + ReadableStreamEventSourceはPOST非対応
状態管理useState + props伝播複雑度が低い、Context不要
APIプロキシnext.config.ts rewritesCORS回避
スタイリングTailwind CSSCLAUDE.MDの規約

MVP外(将来対応)

  • エフェメラルトークンAPI(Gemini Live用)
  • WebSocket接続(Gemini Live用)
  • 音声処理フック(Gemini Live用)
  • E2Eテスト
  • ダークモード

次のステップ

承認後、/nextjs コマンドで実装を開始。


実装完了レポート

実装サマリー

  • 実装日: 2026-01-25
  • 変更ファイル数: 19 files
  • 技術スタック: Next.js 16.1.4 + React 19.2.3 + TypeScript 5.x + Tailwind CSS 4.x

既存の web/index.html(バニラJS、約1005行)を Next.js 15 + React 19 + TypeScript に完全移植完了。全6コマンド(plan, fullstack, go, nextjs, discuss, research)の実行、SSEストリーミング、質問UI、計画承認UI、ファイルセレクター、セッション継続機能を実装。

変更ファイル一覧

ファイル変更内容
frontend/package.jsonNext.js 16.1.4, React 19.2.3, Tailwind CSS 4 等の依存関係定義
frontend/tsconfig.jsonTypeScript strict mode 設定
frontend/tailwind.config.tsTailwind CSS 設定
frontend/next.config.tsAPIプロキシ設定(localhost:8080へのrewrites)
frontend/src/app/layout.tsxルートレイアウト(メタデータ、フォント設定)
frontend/src/app/page.tsxメインページ(状態管理の中心、372行)
frontend/src/app/globals.cssグローバルスタイル(Tailwind directives)
frontend/src/types/index.ts型定義(StreamEvent, Question, ToolInput等)
frontend/src/lib/constants.ts定数定義(COMMANDS, DEV_FOLDERS, PLAN_APPROVAL_KEYWORDS)
frontend/src/lib/api.tsAPI呼び出し関数(fetchFiles, executeCommandStream, continueSessionStream)
frontend/src/hooks/useSSEStream.tsSSEストリーム処理フック(TextDecoder stream:true、不完全行バッファリング)
frontend/src/hooks/useSessionManagement.tsセッション・コスト・プロジェクトパス管理フック
frontend/src/hooks/useFileSelector.tsファイル選択フック(5フォルダ対応)
frontend/src/components/CommandForm.tsxコマンド入力フォーム(プロジェクトパス、コマンド選択、ファイル選択、引数入力)
frontend/src/components/ProgressContainer.tsx進捗表示コンテナ(イベントリスト、質問、結果を統括)
frontend/src/components/EventList.tsxイベントリスト表示(自動スクロール対応)
frontend/src/components/EventItem.tsx個別イベント表示(ツール別アイコン、展開可能テキスト)
frontend/src/components/QuestionSection.tsx質問UI(単一選択/複数選択、カスタム入力対応)
frontend/src/components/PlanApproval.tsx計画承認UI(Approve/Rejectボタン、isLoading対応)
frontend/src/components/LoadingIndicator.tsxローディングスピナー表示
frontend/docs/screens.md画面一覧ドキュメント
frontend/docs/screen-flow.md画面遷移フロードキュメント

計画からの変更点

実装計画に記載がなかった判断・選択:

  • Next.js バージョン: 計画では Next.js 15 としていたが、create-next-app@latest により Next.js 16.1.4 が採用された
  • React バージョン: 計画では React 19 としていたが、React 19.2.3 が採用された
  • ドキュメント追加: frontend/docs/screens.mdfrontend/docs/screen-flow.md を追加作成(計画外)
  • DisplayEvent型の追加: イベント表示用の内部型として DisplayEvent インターフェースを追加
  • EventDotType型の追加: イベントドットの色分け用に EventDotType 型を追加

実装時の課題

ビルド・テストで苦戦した点

  • substr の非推奨警告: レビューで substr が非推奨であることを指摘され、substring に変更
  • 未使用コードの検出: useSSEStream の初期実装で未使用のインポートがあり、リンターで検出・削除

技術的に難しかった点

  • SSEバッファ処理: マルチバイト文字(日本語)が途中で切れた場合の処理。TextDecoderのstream: trueオプションで解決
  • ツール使用イベントの表示フォーマット: 各ツール(Read, Write, Edit, Glob, Grep, Bash, Task等)に応じた適切な情報表示の設計

残存する懸念点

今後注意が必要な点:

  • 環境変数によるAPIエンドポイント切り替え: 現在はnext.config.tsで固定的にlocalhost:8080へプロキシしているが、本番環境ではNEXT_PUBLIC_API_URL環境変数による切り替えが必要
  • 認証機能: 現在は認証なしで動作しており、本番運用にはNextAuth.js等の認証実装が必要
  • E2Eテスト: 計画のMVP外として記載されているが、品質保証のため将来的に必要
  • web/index.html の削除: A案の方針により、Next.js完成後は既存の web/index.html を削除する予定だが、まだ未実施

動作確認フロー

1. バックエンドを起動
   cd /Users/user/Ghostrunner/backend
   go run ./cmd/server

2. フロントエンドを起動
   cd /Users/user/Ghostrunner/frontend
   npm run dev

3. ブラウザで http://localhost:3000 にアクセス

4. 以下の機能を確認:
   a. プロジェクトパスを入力(例: /Users/user/Ghostrunner)
   b. コマンドを選択(plan, fullstack, go, nextjs, discuss, research)
   c. ファイルセレクターでファイルを選択(任意)
   d. 引数を入力
   e. "Execute Command" ボタンをクリック
   f. イベントリストにツール使用状況が表示されることを確認
   g. 質問が来た場合、選択肢をクリックまたはカスタム入力で回答
   h. 計画承認が必要な場合、Approve/Rejectボタンが表示されることを確認
   i. 完了後、結果出力とセッションID、累計コストが表示されることを確認

デプロイ後の確認事項

  • 本番環境のAPIエンドポイント設定(環境変数)
  • CORSの設定確認
  • SSEストリーミングの動作確認(プロキシ経由)
  • 全6コマンドの動作確認
  • 日本語を含む長いテキストの表示確認
  • モバイル端末での表示確認
  • web/index.html の削除(Next.js版への完全移行後)