> [!left-bubble] ![[chappy.webp]]
> [[GPT-5.4]] high での調査・対応結果です。**対応はしませんでした。**
**この対応をすると以下の問題が発生するため。**
- マウスカーソルが中央に移動しなくなる
- 切り替え後の操作が遅れる
- [[Window Hints (JINRAI)|Window Hints]]からの `Focus Back` が動かなくなる
解決したい問題に対して失うものが大きい。
## 概要
Focus Back で別ワークスペース上の Google Chrome ウィンドウへ戻るとき、移動前ワークスペースに残っている別の Chrome ウィンドウがいったんアクティブになってからワークスペースが切り替わる。
期待値は、移動前ワークスペースの別ウィンドウを経由せず、移動後ワークスペースにある戻り先 Chrome ウィンドウがそのままアクティブになること。
## 計画
- [x] `focus_back` と `focus_history` の実装を確認し、別ワークスペース遷移時のフォーカス処理が `hs.window:focus()` 任せになっている箇所を特定する。
- [x] Hammerspoon の `hs.window` / `hs.spaces` API の制約を確認し、対象ウィンドウの Space へ先に移動してから再フォーカスする方式が安全か判断する。
- [x] 修正方針を提示して承認を得たあと、必要ならテストケースを追加してから実装する。
## 総括
### 対応内容
- タスクノートを作成し、今回の不具合内容と調査計画を整理した。
- `focus_back.lua` / `focus_history.lua` / 関連テストを確認し、戻り先ウィンドウへのフォーカスが `hs.window:focus()` の単発呼び出しに依存していることを確認した。
- Hammerspoon ドキュメントを確認し、`hs.spaces.windowSpaces()` と `hs.spaces.gotoSpace()` を使って対象 Space へ先に移動してから再フォーカスする方向が妥当と判断した。
- `focus_history.lua` に cross-space 向けの Space 判定・遷移・再フォーカス処理を実装した。
- `spec/helpers/hs_focus_back_mock.lua` を拡張し、Space 状態・`gotoSpace`・横スライドキー・`doAfter` をモックできるようにした。
- `spec/focus_back_spec.lua` に隣接 Space / 非隣接 Space / 履歴汚染防止 / Space 情報欠落時フォールバックのテストを追加し、`busted` 全体が通ることを確認した。
- `window_hints.lua` の `navigation.focusBackKey` 経路も完了コールバックベースへ揃え、cross-space 時に hints クローズやカーソル移動が早すぎないよう修正した。
- `spec/window_hints_spec.lua` を更新し、preview 解決と互換フォールバックの挙動を調整した。
- `window_hints` 表示中の `keyBlocker` が cross-space 用の `ctrl+fn+←/→` を飲み込んでいたため、`focusBackKey` 実行前に blocker を解除する修正を追加した。
- `window_hints` の `onFocused` 後処理で `frame()` 取得に失敗しても hint が残らないよう、先に `closeHints(true)` を呼ぶ順序へ修正した。
### 対応による恩恵
- 実装前に修正ポイントを `focus_history` に絞り込めたため、影響範囲を抑えて対応できる見込みが立った。
- 隣接 Space では Mission Control 風の直接ジャンプではなく横スライド遷移を優先し、見た目の自然さを保てるようになった。
- 非隣接 Space では従来より確実に戻り先ウィンドウへ再フォーカスするため、同一アプリ別ウィンドウの一時的な前面化が残りにくくなった。
### 詳細
現状の Focus Back は `focus_history:focusBack()` 内で戻り先ウィンドウに対して `targetWindow:focus()` を直接呼んでいる。
Hammerspoon ドキュメント上も `hs.window:focus()` は単に対象ウィンドウへフォーカスする API であり、別 Space 上の同一アプリ内ウィンドウへ移る際に一時的な別ウィンドウ活性化を抑止する仕組みは見当たらなかった。
最終的に、戻り先ウィンドウが現在 Space と異なる Space にある場合は、以下の流れで実装した。
1. `hs.spaces.windowSpaces(targetWindow:id())` で戻り先 Space を取得する。
2. `hs.spaces.focusedSpace()` と比較し、隣接 Space なら `ctrl+fn+←/→`、非隣接なら `hs.spaces.gotoSpace(targetSpace)` を使う。
3. Space 切り替え後に `doAfter` ベースのリトライで戻り先ウィンドウへ再度 `focus()` を試みる。
4. 切り替え中は `switching` フラグで `windowFocused` 通知を無視し、履歴がノイズで汚れないようにする。
この方針なら、移動前 Space に残っている同アプリ別ウィンドウが前面化したままになる時間を減らし、最終的に期待する Chrome ウィンドウをアクティブにしやすい。
### 備考・注意事項
- 実機での最終確認は未実施。
- Space 情報が取れない環境では安全側に倒して従来どおり直接 `focus()` へフォールバックする。
- `navigation.focusBackKey` も同じ `focusHistory` 経路を使うため、今後は `focus_back` 本体と同じ cross-space タイミングで完了後処理が走る。
- `navigation.focusBackKey` 実行時は、内部で送る Space 移動キーが hints のキーブロッカーに阻害されない。
### 参考リンク
- Hammerspoon `hs.window:focus`
- Hammerspoon `hs.spaces.windowSpaces`
- Hammerspoon `hs.spaces.gotoSpace`
## 作業メモ
### cross-space focus 実装開始 2026-03-21 19:38
`focus_history` に Space 切り替え制御を追加し、隣接 Space は横スライド、非隣接は `gotoSpace` を使う実装とテスト追加に着手する。
### 実装とテスト完了 2026-03-21 19:45
`focus_history.lua` / `spec/helpers/hs_focus_back_mock.lua` / `spec/focus_back_spec.lua` を更新し、`busted` が 137 success で通過した。
### window_hints の追補修正 2026-03-21 19:52
`navigation.focusBackKey` が cross-space 非同期化に追従できていなかったため、完了コールバックへ寄せる修正を追加し、`busted` が 139 success で通過した。
### window_hints の blocker 修正 2026-03-21 20:03
`navigation.focusBackKey` で何も起きない原因は hints 表示中の `keyBlocker` が `ctrl+fn+←/→` を飲み込んでいたためだった。`focusBack` 実行前に blocker を解除するよう修正し、`busted` は引き続き 139 success で通過した。
### window_hints の close 順修正 2026-03-21 20:11
cross-space 移動後に hint が残る問題に対して、`onFocused` コールバックの先頭で `closeHints(true)` を呼ぶように変更した。これで後続の `frame()` や `onSelect` が不安定でも hint は先に消える。