Skip to content

Enter custom command選択後、カスタムコマンド入力中にセレクターが再表示されEnterで送信できない #288

@Kewton

Description

@Kewton

概要

メッセージ入力欄のスラッシュコマンドセレクターで「Enter custom command...」を選択した後、カスタムコマンドを入力してもEnterキーで送信できない。入力欄に「/」がセットされ文字の追加入力は可能だが、入力のたびにコマンドセレクターが再表示されるため、Enterキーがセレクター操作に消費されて送信されない。CodexとClaude Codeの両方で発生する。

再現手順

  1. Worktree詳細画面を開く(Codex or Claude Codeセッション)
  2. メッセージ入力欄に / を入力し、スラッシュコマンドセレクターを表示
  3. 「Enter custom command...」ボタンをクリック
  4. セレクターが閉じ、入力欄に / がセットされる
  5. 続けてコマンド名を入力(例: model)→ 入力欄は /model になる
  6. この時点でコマンドセレクターが再表示される
  7. Enterキーを押す → セレクターの操作として処理され、メッセージが送信されない

期待する動作

「Enter custom command...」選択後にカスタムコマンド(例: /model gpt-4o)を入力し、Enterキーで正常にCLIセッションへ送信できる。

実際の動作

  • 「Enter custom command...」選択後、/ の後に文字を入力するとコマンドセレクターが再度表示される
  • セレクターが開いている間はEnterキーがセレクター操作に消費され、メッセージ送信ができない
  • 結果として、カスタムコマンド機能が実質的に使用不可能な状態

根本原因の仮説

src/components/worktree/MessageInput.tsx L158 の handleMessageChange 内のセレクター表示ロジックに問題がある。

// L157-162
if (newValue === '/' || (newValue.startsWith('/') && !newValue.includes(' '))) {
  setShowCommandSelector(true);  // ← handleFreeInput()で閉じても再表示される
} else {
  setShowCommandSelector(false);
}

handleFreeInput()(L141-148)はセレクターを閉じて / をセットするが、その後の入力で handleMessageChange が呼ばれるたびに、メッセージが / で始まりスペースを含まない場合にセレクターが再表示されてしまう。

さらに、src/components/worktree/SlashCommandSelector.tsx のグローバルキーボードリスナー(document.addEventListener('keydown', handleKeyDown) L124-129)が、セレクターが開いている間にEnterキーを e.preventDefault() で消費する。このため、MessageInput.tsx L192 の !showCommandSelector ガードと合わせて、Enterキーイベントがダブルブロックされている。対策案の isFreeInputMode フラグによりセレクターを非表示(isOpen=false)にすれば、このグローバルリスナーも無効化されるため両方の問題が同時に解消される。

対策案

handleFreeInput() 呼び出し時にフリー入力モードのフラグ(例: isFreeInputMode)をセットし、handleMessageChange 内でこのフラグが true の場合はセレクターを再表示しないようにする。

フラグのリセット条件

isFreeInputMode フラグは以下の3つの条件でリセット(false に戻す)する:

  1. submitMessage(): メッセージ送信完了時にリセット
  2. メッセージが空文字になった時: handleMessageChange 内で newValue === '' をチェックしてリセット(ユーザーが手動で全削除した場合を含む)
  3. handleCommandCancel(): コマンドセレクターのキャンセル操作時にリセット

handleMessageChange内の処理フロー

const handleMessageChange = (newValue: string) => {
  setMessage(newValue);

  // フリー入力モードのリセット判定
  if (newValue === '') {
    setIsFreeInputMode(false);
    setShowCommandSelector(false);
    return;
  }

  // フリー入力モード中はセレクター表示をスキップ
  if (isFreeInputMode) {
    return;
  }

  // 通常のセレクター表示ロジック
  if (newValue === '/' || (newValue.startsWith('/') && !newValue.includes(' '))) {
    setShowCommandSelector(true);
  } else {
    setShowCommandSelector(false);
  }
};

主な変更点

  1. isFreeInputMode ステートの追加(useState<boolean>(false)
  2. handleFreeInput() でフラグを true に設定
  3. handleMessageChange() でフラグ true 時にセレクター表示をスキップ、空文字時にフラグをリセット
  4. submitMessage() でフラグをリセット
  5. handleCommandCancel() でフラグをリセット

影響範囲

変更対象ファイル

ファイル 変更内容
src/components/worktree/MessageInput.tsx isFreeInputMode ステート追加、handleFreeInput/handleMessageChange/submitMessage/handleCommandCancel の修正

関連コンポーネント

  • src/components/worktree/SlashCommandSelector.tsx(変更不要。isOpen=false によりグローバルkeydownリスナーの handleKeyDown 内で早期リターン(L95: if (!isOpen) return)されるため、Enterキーイベントが消費されなくなる)
  • src/lib/standard-commands.ts(変更不要だが関連)
  • src/components/worktree/WorktreeDetailRefactored.tsx(変更不要。MessageInputのpropsインターフェースに変更なし。isFreeInputMode は内部状態のため破壊的変更なし)

テスト観点の影響

  • tests/unit/components/worktree/MessageInput.test.tsx: isFreeInputMode フラグの動作検証テストケースの追加が必要(詳細は受入条件のテスト要件を参照)
  • tests/unit/components/SlashCommandSelector.test.tsx: isOpen=false 時にグローバルkeydownリスナーがEnterイベントを消費しないことを検証するテストケースの追加を推奨(現状はisOpen=true時のEscapeキーテストのみ存在)

受入条件

機能要件

  • 「Enter custom command...」選択後、カスタムコマンドを入力してEnterキーで送信できる
  • カスタムコマンド入力中にコマンドセレクターが再表示されない
  • 通常の / 入力時のコマンドセレクター表示は従来通り動作する
  • Codex・Claude Code両方で正常に動作する
  • 既存のスラッシュコマンド選択機能に影響がない
  • フリー入力モード中にメッセージを全削除した場合、再度 / を入力するとセレクターが正常に表示される

テスト要件

  • isFreeInputMode フラグの動作を検証するユニットテストが MessageInput.test.tsx に追加されていること。具体的には以下のテストケースを含むこと:
    • (1) handleFreeInput 呼び出し後にカスタムコマンドを入力しても showCommandSelectorfalse のままであること
    • (2) handleFreeInput 後にEnterキーで submitMessage が呼ばれること
    • (3) メッセージ全削除後に再度 / を入力するとセレクターが表示されること
    • (4) submitMessage 後に isFreeInputMode がリセットされること

レビュー履歴

イテレーション 1 (2026-02-17)

  • SF-1: Issueタイトルを問題の本質に合わせて修正(「Codexにてカスタムモデルが利用出来ない」→「Enter custom command選択後、カスタムコマンド入力中にセレクターが再表示されEnterで送信できない」)
  • SF-2: 対策案にisFreeInputModeフラグの具体的なリセット条件(submitMessage時/空文字時/handleCommandCancel時)とhandleMessageChange内の処理フローを明記
  • SF-3: 根本原因にSlashCommandSelectorのグローバルkeydownリスナーによるEnterキーのe.preventDefault()消費を補足説明として追加

イテレーション 2 (2026-02-17) - 影響範囲レビュー反映

  • SF-1: 受入条件に「テスト要件」セクションを追加。isFreeInputModeフラグの動作を検証する具体的なユニットテストケース4項目を明記
  • SF-2: 影響範囲に「テスト観点の影響」セクションを追加。MessageInput.test.tsxのテスト追加必要性と、SlashCommandSelector.test.tsxのisOpen=false時のEnterキー透過テスト推奨を記載
  • NTH-1: 影響範囲の関連コンポーネントにWorktreeDetailRefactored.tsxへの破壊的変更がない旨を明記

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions