switcha
WIP(現在進行中)
Work In Progress
このプロジェクトで現在進行中の作業と、過去のスナップショットを記録する。
現在の状況
switcha は DockDoor の代替となる、ゼロから新規開発の macOS ウィンドウ管理アプリ。 コア機能(Dock ホバープレビュー/Cmd+Tab スイッチャー)が、サムネイル表示と クリック操作まで揃い、実用アプリとして一通り完成している。
完了:
- フェーズ1〜2: プロジェクト雛形、ウィンドウ列挙+ウォームキャッシュ
- 安定署名(個人チーム証明書。リビルドしても TCC 権限が保持される)
- フェーズ3: Dock ホバープレビュー(マウス幾何検証で誤発火を解消、ScreenCaptureKit の 実サムネイル表示、カードのクリックで切り替え)
- フェーズ4: Cmd+Tab スイッチャー(ネイティブ Cmd+Tab を抑止、サムネイルグリッド表示、 キーボード操作、カードのクリックで切り替え)
次にやること(いずれも磨き込み。急ぎではない):
- スイッチャーの並び順を使用履歴順にする改善
- 多数ウィンドウ時のスクロール対応
- 最小限の設定画面(プレビュー遅延・サイズ等の項目が固まったら)
詰まっていること・未決事項:
- 特になし。コア機能はすべて実機で動作確認済み。
過去のWIPアーカイブ
(新しい「現在の状況」を書く前に、古いものをここに追記でアーカイブする。新しいものが上)
2026-05-22 16:52 時点のスナップショット
コア機能2つともが基本形で動作する状態。完了: フェーズ1〜2、安定署名、フェーズ3 (Dock ホバープレビュー。誤発火解消+実サムネイル表示)、フェーズ4チャンク1(Cmd+Tab スイッチャー基本形。一覧はテキスト表示)。
次にやること: フェーズ4チャンク2(スイッチャーのサムネイル化・並び順改善・スクロール)。 詰まっていること: 特になし。
2026-05-22 16:23 時点のスナップショット
switcha は DockDoor の代替となる、ゼロから新規開発の macOS ウィンドウ管理アプリ。 コア機能はDockホバープレビューとCmd+Tabスイッチャーの2つ。
完了: フェーズ1(雛形)、フェーズ2(ウィンドウ列挙+ウォームキャッシュ)、安定署名、 フェーズ3チャンク1(DockObserver+マウス幾何検証)、フェーズ3チャンク2(プレビュー パネル表示。サムネイルはまだグレー枠)。
次にやること: フェーズ3チャンク3(ScreenCaptureKit でサムネイル取得しグレー枠を実画像に)。
詰まっていること: 特になし。メニューバーアイコンがノッチで隠れる件は macOS の仕様で、 当面は Finder をアクティブにして確認する運用。
ROADMAP(計画)
ロードマップ
今週
- フェーズ1: プロジェクト雛形(メニューバー常駐+権限オンボーディング)
- フェーズ2: ウィンドウ列挙+ウォームキャッシュ
- 安定署名のセットアップ
- フェーズ3チャンク1: DockObserver(Dock 監視+マウス幾何検証)
- フェーズ3チャンク2: プレビューパネル表示
- フェーズ3チャンク3: ScreenCaptureKit でサムネイル取得
- フェーズ4チャンク1: Cmd+Tab スイッチャー基本形(ネイティブ抑止+ウィンドウ切替)
- フェーズ4チャンク2: スイッチャーをサムネイル付きグリッドに刷新
- フェーズ5チャンク1: プレビュー/スイッチャーのカードをクリックで切り替え
今月
- スイッチャーの並び順を使用履歴順にする
- 多数ウィンドウ時のスクロール対応
- 最小限の設定画面(設定項目が固まったら)
今四半期
- 配布の検討(notarization。無料の個人チームでは不可のため、有料登録の要否を判断)
いつか
- 設定項目の拡充(プレビューの表示遅延・サイズ・対象アプリのフィルタ等)
- メニューバーアイコンがノッチで隠れる問題への対応案の検討
DECISIONS(意思決定)
意思決定記録
このプロジェクトで下した重要な意思決定を記録する。 最新が上に来る。
2026-05-22: Cmd+Tab はネイティブを抑止して switcha のスイッチャーに置き換える
背景: ウィンドウスイッチャーの起動キーに、別キーバインド(Option+Tab 等)を割り当てる 案と、Cmd+Tab そのものを置き換える案があった。
決定: CGEvent タップで Cmd+Tab の keyDown を消費し、ネイティブのアプリスイッチャーを 抑止して、switcha のウィンドウスイッチャーに置き換える。
理由: ユーザーが慣れた Cmd+Tab をそのままウィンドウ単位の切り替えにしたいため。実機で keyDown の消費によりネイティブスイッチャーを問題なく抑止できることを確認した。
2026-05-22: DockDoor をフォークせずゼロから新規開発する
背景: 既存 OSS の DockDoor を改良したいが、フォーク(GPL-3.0 を継承)か、ゼロからの 新規開発かの選択があった。
決定: ゼロから Swift で新規開発する。DockDoor のコードはコピーせず、調査で得た知見のみ活用する。
理由: ライセンスの自由度を確保し、最小構成のクリーンなコードベースにするため。難所 (Accessibility・ScreenCaptureKit・権限処理)を自前実装するコスト増は受け入れる。
2026-05-22: コード署名は個人チームの Apple Development 証明書で安定署名する
背景: ad-hoc 署名はリビルドのたびに署名が変わり、アクセシビリティ等の TCC 権限が毎回 外れて開発効率が悪い。
決定: Xcode に個人 Apple ID を追加し、Personal Team(Team ID: 645K6U83T5)の Apple Development 証明書で署名する。
理由: 署名が安定し、TCC 権限がリビルドをまたいで保持される。組織アカウント(Thomson Inc.) ではなく個人チームを選んだのは switcha が個人プロジェクトのため。無料チームは配布用の notarization ができない点は、配布フェーズで改めて検討する。
2026-05-22: ウィンドウのサムネイル取得は ScreenCaptureKit(公開API)を使う
背景: ウィンドウのプレビュー画像取得に、高速なプライベートAPI(CGSHWCaptureWindowList 等) と公開APIの選択があった。
決定: ScreenCaptureKit(公開API)を使い、プライベートAPIには依存しない。
理由: switcha は将来の配布・公開も視野に入れており、プライベートAPI依存は notarization・ 審査・安定性の面でリスクになるため。
2026-05-22: Dock ホバー検出はマウス座標の幾何検証を必須にする
背景: DockDoor は Dock が報告する「選択中の Dock アイテム」を鵜呑みにしており、アイコン 周辺にカーソルを置いただけでプレビューが誤表示される不具合があった。
決定: Dock の AX 選択変更通知は「ホバーが変わったかもしれない」きっかけとしてのみ使い、 選択された Dock アイテムの AX フレーム(アイコン実寸)にマウス座標が入っているかを必ず 検証してから採用する。
理由: DockDoor の誤発火を設計段階で回避するため。検証により、アイコン外のホバーを正しく 却下できることを実機で確認済み。
DEVLOG(作業ログ)
開発日誌
このプロジェクトでの作業を時系列で記録する。 最新のエントリが上に来る。
2026-05-22
17:15 - スイッチャーのサムネイル化とクリック操作(フェーズ4チャンク2・フェーズ5チャンク1)
やったこと:
- フェーズ4チャンク2: Cmd+Tab スイッチャーをテキスト一覧からサムネイル付きカードの
グリッドに刷新。SwitcherModel(ObservableObject)を導入し、selectedIndex のみ
@Publishedにすることで、Tab 連打でも読み込み済みサムネイルがちらつかないように した。各カードにアプリアイコン+タイトル、下部に選択中ウィンドウの完全タイトルを表示 - フェーズ5チャンク1: ウィンドウ前面化の共通処理を WindowActivator に集約。Dock プレビューと Cmd+Tab スイッチャーのカードをクリック可能にし、クリックで対象 ウィンドウへ切り替わるようにした
- 実機確認: スイッチャーのグリッド表示・サムネイルのちらつきなし、プレビュー/ スイッチャーどちらもクリック1回でウィンドウ切り替えできることを確認
気づき:
.nonactivatingPanelのおかげで、フローティングパネル内のボタンも1回目のクリックで 反応する(first-mouse 対応の追加実装は不要だった)
次回やること:
- 残りは磨き込み(最小限の設定画面、スイッチャーの並び順を使用履歴順に、多数 ウィンドウのスクロール対応など)。コア機能は完成済み
16:52 - サムネイル表示とCmd+Tabスイッチャー(フェーズ3チャンク3・フェーズ4チャンク1)
やったこと:
- フェーズ3チャンク3: ThumbnailCache(actor)を実装。ScreenCaptureKit でウィンドウを
撮影し CGImage をキャッシュ。各ウィンドウカードが
.taskで非同期にサムネイルを 読み込み、プレビューのグレー枠を実画像に置き換えた。画面収録権限を付与して動作確認 - フェーズ4チャンク1: Cmd+Tab ウィンドウスイッチャーの基本形を実装
- SwitcherController: CGEvent タップで Cmd+Tab を捕捉・消費してネイティブのアプリ スイッチャーを抑止。Tab/Shift+Tab で選択を送り、Cmd を離すと選択ウィンドウを 前面化、Esc で取消
- SwitcherOverlayView: ウィンドウ一覧をテキスト行で表示(サムネイルは後続チャンク)
- 一覧は WindowCache(ウォームキャッシュ)+CGWindowList の前面順から即座に構築
- ネイティブ Cmd+Tab の抑止が効くこと、ウィンドウ単位の切り替えが動くことを実機で確認
気づき:
- CGEvent タップで Cmd+Tab の keyDown を消費すれば、ネイティブのアプリスイッチャーは 問題なく抑止できた
次回やること:
- フェーズ4チャンク2: スイッチャーにサムネイル表示、並び順(使用履歴順)の改善、 多数ウィンドウ時のスクロール対応
16:23 - プロジェクト立ち上げ〜Dockホバープレビュー(フェーズ1〜3チャンク2)
やったこと:
- DockDoor(ejbills/DockDoor, GPL-3.0)を
~/ichirokisanuki/dockdoor-refに clone して調査。 Cmd+Tab の重さの原因(スイッチャー起動のたびに同期でウィンドウ列挙+スクリーンショット撮影)と、 Dock プレビュー誤発火の原因(Dock の選択状態を鵜呑みにし、マウス座標の幾何検証をしていない)を特定 - フェーズ1: Xcode プロジェクト雛形を作成(XcodeGen 管理、Makefile、メニューバー常駐アプリ、 アクセシビリティ/画面収録の権限オンボーディング)
- フェーズ2: ウィンドウモデル(WindowInfo)、列挙(WindowEnumerator: AX + CGWindowList)、 ウォームキャッシュ(WindowCache: イベント+定期更新)を実装・検証
- 安定署名のセットアップ: Xcode に個人 Apple ID を追加し、Personal Team の Apple Development 証明書を作成。project.yml を ad-hoc から安定署名へ変更し、リビルドしても TCC 権限が 保持されることを実証
- フェーズ3チャンク1: DockObserver を実装。Dock の AX 選択変更通知をきっかけに、選択された Dock アイテムの AX フレームにマウス座標が入っているかを幾何検証。誤発火を 29 回却下できることを確認
- フェーズ3チャンク2: PreviewController/PreviewContentView を実装。Dock アイコンの真上に フローティングパネルでプレビュー表示。生存ポーリングでアイコン↔プレビュー間の移動でも ちらつかないようにした
詰まったこと/気づき:
- 14 インチ MBP のノッチ+メニューバー混雑で switcha のメニューバーアイコンが隠れた。 アプリメニューの少ない Finder をアクティブにすると表示される
- ad-hoc 署名はリビルドのたびに署名が変わり TCC 権限が外れる。署名方式の変更後は古い TCC
エントリが残るため、
tccutil reset Accessibility com.ichirokisanuki.switchaでリセット してから付与し直す必要があった
次回やること:
- フェーズ3チャンク3: ScreenCaptureKit でウィンドウのサムネイルを取得し、プレビューの グレー枠を実画像に置き換える
最近のコミット
README
switcha
macOS 用の軽量なウィンドウ切り替え / Dock プレビューアプリ。
OSS の DockDoor に着想を得つつ、 自分に必要な最小機能だけを、ゼロから新規実装したもの。
コア機能
- Dock ホバープレビュー — Dock アイコンにカーソルを合わせると、そのアプリの各ウィンドウのプレビューを表示する
- Cmd+Tab ウィンドウスイッチャー — アプリ単位ではなくウィンドウ単位で切り替える
セットアップ
ビルドには フル Xcode(Command Line Tools だけでは不可)と XcodeGen が必要。
brew install xcodegen
make build # project.yml から .xcodeproj を生成してビルド
make run # ビルドして起動
.xcodeproj は project.yml から生成するため Git では追跡しない。
Xcode で開きたい場合は make generate 後に switcha.xcodeproj を開く。
使い方
初回起動時に アクセシビリティ と 画面収録 の権限を求められる。 メニューバーアイコンから許可状況を確認・設定できる。
動作環境
- macOS 14.0 以降 / Apple Silicon