Skip to content

develop → main: fix サイドバーからのブランチ遷移とtooltip 残留 (#675, #676)#679

Merged
Kewton merged 6 commits intomainfrom
develop
Apr 24, 2026
Merged

develop → main: fix サイドバーからのブランチ遷移とtooltip 残留 (#675, #676)#679
Kewton merged 6 commits intomainfrom
develop

Conversation

@Kewton
Copy link
Copy Markdown
Owner

@Kewton Kewton commented Apr 24, 2026

Summary

サイドバー操作時の 2 件のバグ修正を main にリリースします。両 issue は本会話のオーケストレーションで根本原因分析→修正→PR→マージまで自動実行され、developにマージ・統合検証済み。

含まれる変更

Issue PR commit ファイル
#675 #677 `e91e1928` `WorktreeDetailRefactored.tsx`, `useFileTabs.ts`, `useFileTabs.test.ts`
#676 #678 `54d62d91` `BranchListItem.tsx`, `Sidebar.test.tsx`, `BranchListItem.test.tsx`

加えて両 issue の調査・作業計画・TDD・受入・進捗レポート(`dev-reports/bug-fix/` 配下)を含む。

#675: onDirtyChange 無限ループ修正

根本原因

`useFileTabs` の戻り値が毎レンダー新オブジェクト → `WorktreeDetailRefactored` の 4 つの `useCallback` の deps `[fileTabs]` が毎レンダー再生成 → `FilePanelContent` → `MarkdownEditor` に不安定 prop 伝播 → `useEffect([isDirty, onDirtyChange])` が毎レンダー発火 → `SET_DIRTY` reducer が同値でも新 state を返す → 無限 re-render ループで `router.push` の transition が starve(commit されない)。

修正

  • A 案: 4 つの `useCallback` の deps を `[fileTabs]` → `[fileTabs.dispatch]` に変更(dispatch は useReducer 由来で identity stable)
  • B 案: `SET_DIRTY` reducer に同値判定を追加(多層防御)

#676: tooltip 固着修正

根本原因

`mouseleave` 合成イベントと React 18 concurrent rendering の race により、`isTooltipVisible` が `true` のまま固定。focus も触らないので `onBlur` も発火せず tooltip が永久に残る。Issue #675 の無限ループが race を増幅していた。

修正

  • A 案: `isVisible = isTooltipVisible && !isSelected` で選択中ブランチの tooltip を抑止
  • B 案: `onClick` ラッパーで `setIsTooltipVisible(false)` 後に `onClick` を呼ぶ
  • C 案: `isVisible=false` 時に portal 内も `null` で DOM から物理除去

統合検証(develop で実施済)

チェック 結果
npm run lint ✓ No warnings or errors
npx tsc --noEmit
npm run test:unit ✓ 6393 passed / 7 skipped (新規 9 件)
npm run build ✓ Compiled successfully

Test plan

  • `.md` ファイルを worktree で開いた状態 → サイドバーから別ブランチを選択して URL が遷移すること
  • サイドバーのブランチをホバー → tooltip 表示 → クリックで遷移 → tooltip が残らないこと
  • 選択中ブランチをホバーしても tooltip が出ないこと
  • `.png`/`.json`/`.yaml` 等他ファイル種別での遷移回帰なし
  • 自動テスト 6393 件全パスを CI で確認

フォローアップ Issue 候補(受入テストで判明)

#675 の受入テストエージェントの指摘:

  1. `.md` 限定ではない可能性(`.yaml/.yml` 等の editable 拡張子でも再現する可能性)
  2. `WorktreeDetailRefactored.tsx` L551/L570/L581/L875/L904 に同種アンチパターン残存
  3. `useFileTabs` の戻り値構造(毎レンダー新オブジェクト)改善

🤖 Generated with Claude Code

Kewton and others added 6 commits April 24, 2026 20:26
Tooltip was persistently visible after React concurrent re-renders lost
the synthetic mouseleave, leaving `isTooltipVisible=true` stuck on the
previously-hovered branch.

- A: skip rendering the tooltip for the currently selected branch so a
  stale visibility flag does not linger on the focused item.
- B: wrap the button onClick to explicitly reset `isTooltipVisible` to
  false before forwarding to the parent handler.
- C: unmount the portal contents when not visible instead of keeping an
  empty tooltip node in the DOM; only attach aria-describedby while the
  tooltip is actually rendered so screen readers do not chase a dangling
  reference.

Tests updated: existing tooltip-content assertions now fire mouseEnter
first, and new tests cover initial-render absence, selected-branch
absence, click-dismisses-tooltip, focus/blur lifecycle, and
aria-describedby gating.
- useFileTabs: SET_DIRTY short-circuits when target tab isDirty is unchanged,
  so upstream useReducer skips re-render and MarkdownEditor's onDirtyChange
  useEffect no longer feeds a loop.
- WorktreeDetailRefactored: useCallback deps for file-panel handlers switched
  from [fileTabs] to [fileTabs.dispatch] to stabilise callback identity and
  stop MarkdownEditor from re-firing onDirtyChange every render. This unblocks
  router.push transitions when switching branches from the sidebar while a
  .md file is open.
- Adds reducer tests covering same-value SET_DIRTY no-op (false→false, true→true,
  double-apply stable reference).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eptance)

Investigation, work-plan, TDD and acceptance artifacts for the sidebar
tooltip stuck bug fix (A+B+C defensive guard).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… acceptance, progress)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(#675): stop MarkdownEditor re-render loop blocking worktree URL updates
fix(#676): prevent stuck sidebar branch tooltip (A+B+C)
@Kewton Kewton merged commit c1ff90b into main Apr 24, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant