Skip to content

fix(ui): 札の理由と 2 段目の「…」パネルの Esc で重ねたパネルまで閉じないようにする (#3660) - #3665

Merged
Kewton merged 1 commit into
developfrom
feature/3660-worktree
Oct 13, 2026
Merged

Kewton merged 1 commit into
developfrom
feature/3660-worktree

Conversation

@Kewton

@Kewton Kewton commented Oct 13, 2026

Copy link
Copy Markdown
Owner

確かめたこと(直す前に、親から通して描画するテストで実測)

WorktreeDetailRefactored → WorktreeDetailDesktop(実際の ignore list)→
PaneOverlayFrame / usePaneOverlayDismiss、実物の VerificationStatusChip と
ActionBarItemsOverflow を通して描画(#3591 の entry-points テストと同じ組み方)。
Files パネルを重ねる表示で開いた状態で:

  • 札の理由(ⓘ): 開けた。ⓘ 上で Esc 1 回 → 理由とパネルが両方閉じた(再現)。
    札は [data-activity-entry](パネルの Esc 対象領域)で、パネルの document の
    listener が先に登録されているため、札が後から preventDefault しても間に合わない。
  • 「…」パネル: 開けた。chip が折り畳まれて「…」パネルの最初の操作になる幅では、
    開いた直後のフォーカスが札にあり、Esc 1 回 → 「…」パネルと Files パネルが両方
    閉じた(再現)。理由まで開いていると 3 層が 1 回で全部閉じた。
    一方、パネルの最初の操作が札以外(セッションの操作だけが折り畳まれた場合)の
    Esc は、対象がパネルの Esc 領域外なので Files パネルは閉じなかった(陰性で固定)。

決めたこと

  • VerificationStatusChip: data-floating-layer は "true" のまま(既存テスト
    DesktopHeader-one-row-3590.test.tsx:251 が "true" を固定しているため変えない)。
    代わりに、見出しの道具の「…」(HeaderToolBar の handleMoreKeyDown)と同じく
    自分の要素の onKeyDown で Esc を受けて preventDefault する形にした。React の
    handler は root(portal の container を含む)で走り document より先なので、
    パネル・「…」パネルの document listener は isUnhandledEscape で止まる。
    理由を開くときに ⓘ へフォーカスを移す(Safari はクリックでボタンにフォーカス
    しないため、Esc が body に落ちて札の onKeyDown を通らない穴をふさぐ)。
    札の外で押された Esc は従来どおり document の listener で理由を閉じる(陰性)。
  • ActionBarItemsOverflow: 見出しの道具の「…」リストと同じ
    data-floating-layer="open" にした。パネルの listener は「…」パネルより先に
    登録されるため印でないと止まらない(fix(ui): 表示設定メニューを Esc で閉じたらフォーカスを歯車へ戻す (#3650) #3655 と同じ理由)。
    preventDefault は足さなかった: 足すと「…」パネルの後に登録される札の理由の
    document listener が止まり、既存テスト TerminalSplitContainer-header-in-bar-
    3592.test.tsx「closes the reason on an Escape nothing else handled」が落ちた
    (実測)。
    印の副作用: 「…」パネルが開いている間の外側の押下は「…」パネルだけを閉じ、
    Files パネルは残す(ContextMenu / 表示設定メニューと同じ規則。テストで固定)。

PC とスマホ

  • PC: 札は 2 段目の操作バーに popover="floating"、狭い幅では「…」パネルの中。
    どちらも上のテストで確認。
  • スマホ: 理由は出る(popover="inline"、MobileHeader 直下の帯)。重ねるパネル
    (usePaneOverlayDismiss)はスマホに無く、ActionBarItemsOverflow
    (TerminalSplitContainer)も PC のみなので、下に閉じる層は無い。札の変更は共有
    され、理由を閉じた Esc を preventDefault すること(window の下の層に渡さない)と
    閉じた状態の Esc を未処理のまま残すことを札単体のテストで固定した。

テスト

  • 新規 tests/unit/components/worktree/escape-chip-overflow-3660.test.tsx(12 件)。
    直す前の実装で陽性 7 件が失敗、陰性 5 件(理由・「…」を閉じた状態の Esc は 1 回で
    Files パネルを閉じる/札の外の Esc でも理由は閉じる/札以外の操作からの Esc/
    上の層が処理済みの Esc では理由は閉じない)は前後とも通ることを確認。
    DOM のみのテストでファイルを触らないため一時ディレクトリは使っていない。
  • npx vitest run(上記 + escape-stacked-layers-3630 / escape-file-metadata-toggle-
    3650 / VerificationStatusChip / DesktopHeader-* / HeaderToolBar-3591 /
    WorktreeDetailRefactored-entry-points-3591 / -activity-overlay-3589 /
    -mobile-overflow / action-bar-overflow-* / TerminalSplitContainer* /
    verification-* / useActivityPaneOverlay-3589 / guards / docs):
    escape-stacked-layers-3630 を含め通過。guards の
    session-ownership-ws-relay-3334.test.ts の 3 件だけが 5 秒の timeout で落ちた
    (単独で再実行しても同じ)。src/lib のみを import する WS のテストで今回の変更
    (src/components の 2 ファイル)と無関係。
  • npm run lint / npx tsc --noEmit 成功。changelog-fragments check exit 0。

対になる場所を探した結果

  • 札はスマホと PC で同じ部品(上記)。「…」パネルの対は見出しの道具の「…」リスト
    (HeaderToolBar、既に "open" と preventDefault)で変更なし。
  • docs/ の ja / en(webapp-guide.md の ⓘ の記述)に Esc の記述は無かった。
  • 同じ形の残り(WorktreeBreadcrumb / RepositoryTabBar / SortSelectorBase)は
    fix(ui): 表示設定メニューを Esc で閉じたらフォーカスを歯車へ戻す (#3650) #3655 の一覧どおりパネルの Esc 領域外で、今回の範囲外。

本文に無い指摘

  • 本文に無い指摘: tests/unit/guards/session-ownership-ws-relay-3334.test.ts
    「refuses to subscribe…」「types nothing into it…」「does not resize it either」
    がこの環境で 5 秒の timeout で落ちる(今回の変更前のコードに対しても依存なし)。
  • 本文に無い指摘: src/components/worktree/TerminalSplitContainer.tsx の
    ActionBarItemsOverflow は自分が閉じる Esc を preventDefault しないため、window で
    受ける下の層(FilePanelContent の最大化解除)にも同じ押下が届く。札の理由の
    閉じ方を固定した既存テストと両立させるため今回は変えていない。

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

Refs #3660

## 確かめたこと(直す前に、親から通して描画するテストで実測)
WorktreeDetailRefactored → WorktreeDetailDesktop(実際の ignore list)→
PaneOverlayFrame / usePaneOverlayDismiss、実物の VerificationStatusChip と
ActionBarItemsOverflow を通して描画(#3591 の entry-points テストと同じ組み方)。
Files パネルを重ねる表示で開いた状態で:
- 札の理由(ⓘ): 開けた。ⓘ 上で Esc 1 回 → 理由とパネルが両方閉じた(再現)。
  札は [data-activity-entry](パネルの Esc 対象領域)で、パネルの document の
  listener が先に登録されているため、札が後から preventDefault しても間に合わない。
- 「…」パネル: 開けた。chip が折り畳まれて「…」パネルの最初の操作になる幅では、
  開いた直後のフォーカスが札にあり、Esc 1 回 → 「…」パネルと Files パネルが両方
  閉じた(再現)。理由まで開いていると 3 層が 1 回で全部閉じた。
  一方、パネルの最初の操作が札以外(セッションの操作だけが折り畳まれた場合)の
  Esc は、対象がパネルの Esc 領域外なので Files パネルは閉じなかった(陰性で固定)。

## 決めたこと
- VerificationStatusChip: data-floating-layer は "true" のまま(既存テスト
  DesktopHeader-one-row-3590.test.tsx:251 が "true" を固定しているため変えない)。
  代わりに、見出しの道具の「…」(HeaderToolBar の handleMoreKeyDown)と同じく
  自分の要素の onKeyDown で Esc を受けて preventDefault する形にした。React の
  handler は root(portal の container を含む)で走り document より先なので、
  パネル・「…」パネルの document listener は isUnhandledEscape で止まる。
  理由を開くときに ⓘ へフォーカスを移す(Safari はクリックでボタンにフォーカス
  しないため、Esc が body に落ちて札の onKeyDown を通らない穴をふさぐ)。
  札の外で押された Esc は従来どおり document の listener で理由を閉じる(陰性)。
- ActionBarItemsOverflow: 見出しの道具の「…」リストと同じ
  data-floating-layer="open" にした。パネルの listener は「…」パネルより先に
  登録されるため印でないと止まらない(#3655 と同じ理由)。
  preventDefault は足さなかった: 足すと「…」パネルの後に登録される札の理由の
  document listener が止まり、既存テスト TerminalSplitContainer-header-in-bar-
  3592.test.tsx「closes the reason on an Escape nothing else handled」が落ちた
  (実測)。
  印の副作用: 「…」パネルが開いている間の外側の押下は「…」パネルだけを閉じ、
  Files パネルは残す(ContextMenu / 表示設定メニューと同じ規則。テストで固定)。

## PC とスマホ
- PC: 札は 2 段目の操作バーに popover="floating"、狭い幅では「…」パネルの中。
  どちらも上のテストで確認。
- スマホ: 理由は出る(popover="inline"、MobileHeader 直下の帯)。重ねるパネル
  (usePaneOverlayDismiss)はスマホに無く、ActionBarItemsOverflow
  (TerminalSplitContainer)も PC のみなので、下に閉じる層は無い。札の変更は共有
  され、理由を閉じた Esc を preventDefault すること(window の下の層に渡さない)と
  閉じた状態の Esc を未処理のまま残すことを札単体のテストで固定した。

## テスト
- 新規 tests/unit/components/worktree/escape-chip-overflow-3660.test.tsx(12 件)。
  直す前の実装で陽性 7 件が失敗、陰性 5 件(理由・「…」を閉じた状態の Esc は 1 回で
  Files パネルを閉じる/札の外の Esc でも理由は閉じる/札以外の操作からの Esc/
  上の層が処理済みの Esc では理由は閉じない)は前後とも通ることを確認。
  DOM のみのテストでファイルを触らないため一時ディレクトリは使っていない。
- npx vitest run(上記 + escape-stacked-layers-3630 / escape-file-metadata-toggle-
  3650 / VerificationStatusChip / DesktopHeader-* / HeaderToolBar-3591 /
  WorktreeDetailRefactored-entry-points-3591 / -activity-overlay-3589 /
  -mobile-overflow / action-bar-overflow-* / TerminalSplitContainer* /
  verification-* / useActivityPaneOverlay-3589 / guards / docs):
  escape-stacked-layers-3630 を含め通過。guards の
  session-ownership-ws-relay-3334.test.ts の 3 件だけが 5 秒の timeout で落ちた
  (単独で再実行しても同じ)。src/lib のみを import する WS のテストで今回の変更
  (src/components の 2 ファイル)と無関係。
- npm run lint / npx tsc --noEmit 成功。changelog-fragments check exit 0。

## 対になる場所を探した結果
- 札はスマホと PC で同じ部品(上記)。「…」パネルの対は見出しの道具の「…」リスト
  (HeaderToolBar、既に "open" と preventDefault)で変更なし。
- docs/ の ja / en(webapp-guide.md の ⓘ の記述)に Esc の記述は無かった。
- 同じ形の残り(WorktreeBreadcrumb / RepositoryTabBar / SortSelectorBase)は
  #3655 の一覧どおりパネルの Esc 領域外で、今回の範囲外。

## 本文に無い指摘
- 本文に無い指摘: tests/unit/guards/session-ownership-ws-relay-3334.test.ts
  「refuses to subscribe…」「types nothing into it…」「does not resize it either」
  がこの環境で 5 秒の timeout で落ちる(今回の変更前のコードに対しても依存なし)。
- 本文に無い指摘: src/components/worktree/TerminalSplitContainer.tsx の
  ActionBarItemsOverflow は自分が閉じる Esc を preventDefault しないため、window で
  受ける下の層(FilePanelContent の最大化解除)にも同じ押下が届く。札の理由の
  閉じ方を固定した既存テストと両立させるため今回は変えていない。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant