BetterCmdTab

Współtworzenie

Zbuduj BetterCmdTab ze źródeł, uruchom testy i doprowadź pull request do scalenia.

Mile widziane są zarówno zgłoszenia, jak i pull requesty — raporty błędów, pomysły na funkcje oraz zmiany kodu. Ta strona odzwierciedla plik CONTRIBUTING.md w repozytorium; jeśli treści kiedykolwiek się różnią, ten plik jest źródłem prawdy.

Podstawowe zasady

Te cztery zasady nie podlegają negocjacji — PR, który łamie jedną z nich, będzie wymagał zmian bez względu na jakość funkcji.

ZasadaUzasadnienie
Tylko AppKitBez SwiftUI, Catalyst i zewnętrznych frameworków UI. Przełącznik musi działać natywnie i otwierać się natychmiast.
Bez telemetriiBez analityki, raportowania awarii i ruchu sieciowego w tle. Dozwolone są wyłącznie żądania sprawdzające aktualizacje w GitHub Releases.
macOS 13.0 pozostaje minimalną wersjąFunkcje nowszych systemów są chronione przez if #available i mają łagodny mechanizm zastępczy.
Ścieżka krytyczna pozostaje szybkaWszystko, co działa podczas ⌘Tab, wykonuje się poza głównym wątkiem albo jest mierzone.

Budowanie

git clone https://github.com/rokartur/BetterCmdTab.git
cd BetterCmdTab
xcodebuild -scheme "BetterCmdTab Debug" -configuration Debug build

Wymagane są Xcode 16+ i SDK macOS 26. Nowszy materiał panelu jest ograniczony do macOS 26 — budowanie ze starszym SDK nadal działa, ale aplikacja korzysta wtedy z NSVisualEffectView w czasie działania.

Zanim uznasz, że zmiana nie działa, uruchom aplikację raz i przyznaj uprawnienie Dostępność. Bez niego przełącznik się nie uruchamia, więc ⌘Tab po prostu nic nie robi — zobacz Szybki start.

Uruchamianie testów

# whole suite
xcodebuild -scheme "BetterCmdTab Debug" -destination 'platform=macOS' test

# one suite
xcodebuild -scheme "BetterCmdTab Debug" -destination 'platform=macOS' \
  test -only-testing:BetterCmdTabTests/FuzzyMatchTests

Testy używają Swift Testing (import Testing, @Suite / @Test), a nie XCTest. Nie ma metod testXxx(), dlatego pojedynczy przypadek należy wskazać nazwą jego funkcji Swift:

xcodebuild -scheme "BetterCmdTab Debug" -destination 'platform=macOS' \
  test -only-testing:BetterCmdTabTests/FuzzyMatchTests/noMatch

Obejmują czystą logikę — metryki przełącznika, etykiety wierszy, filtrowanie katalogu, dopasowanie rozmyte, analizę aktualizacji, wybór materiału panelu i przenośność ustawień — oraz niewielki zestaw osadzony w AppKit (TabStripWindowingTests, SwitcherReflowTests), który wymaga działającego WindowServer i wyłączonej opcji Ogranicz ruch w macOS. Reszta UI jest sprawdzana ręcznie: przełącznik potrzebuje uprawnienia Dostępność, dlatego ten obszar nie działa w środowisku bez interfejsu graficznego i nie należy do testów jednostkowych.

Gdzie znajdują się poszczególne elementy

Kod jest niewielki. Od tych plików warto zacząć lekturę.

ŚcieżkaDziałanie
Input/HotkeyTap.swiftGlobalne przechwytywanie CGEvent we własnym wątku; wykrywa skrót ⌘Tab i blokuje natywny przełącznik
Switcher/SwitcherController.swiftMaszyna stanów — zaznaczanie, skok po literze, wyszukiwanie, przechodzenie do kart
Switcher/SwitcherView.swiftUkłada widoki listy / siatki / podglądu okien
Switcher/SwitcherPanel.swiftNieaktywujący panel i przypinanie jego aktywnego stanu
Catalog/AppCatalog.swiftWylicza aplikacje i okna przez Accessibility API
Catalog/AppCatalogCache.swiftPrzyrostowa pamięć podręczna zasilana obserwatorami AX i aktualizacjami MRU
Windows/Activator.swiftAktywowanie, podnoszenie, zamykanie, ukrywanie, kończenie
Windows/MRUTracker.swiftKolejność według ostatniego użycia
System/PrivateAPIs.swiftCały prywatny kod CGS / SkyLight, odizolowany w jednym pliku na potrzeby przeglądu
Settings/Natywne okno ustawień AppKit, jeden kontroler na panel

Dodawanie preferencji

Preferencje są kontraktem, a nie tylko zmienną — trzeba zaktualizować kilka elementów.

  1. Dodaj klucz do wyliczenia Keys w App/Preferences.swift, z prefiksem Switcher.. Ciąg klucza jest kontraktem: CatalogFilter i SwitcherController odczytują niektóre klucze bezpośrednio z UserDefaults w wątku tła, więc zmiana nazwy wymaga aktualizacji obu stron.
  2. Dodaj odpowiednią właściwość @Published z didSet, który ją zapisuje. Zabezpiecz zapis przez guard oldValue != newValue else { return } — to zabezpieczenie powstrzymuje reloadFromDefaults() przed ponownym zapisywaniem każdego klucza podczas importu.
  3. Dodaj odpowiedni odczyt do reloadFromDefaults() wraz z wartością domyślną.
  4. Udokumentuj ją w App/ConfigSchemaDocs.swift. Zasila to generowany schema.json, więc pominięty tutaj klucz jest prawidłowy, ale nieudokumentowany — bez uzupełniania w edytorze i dokumentacji po wskazaniu kursorem.
  5. Udostępnij ją we właściwym panelu ustawień. Wszystko, co niestabilne lub nowe, jest domyślnie wyłączona, a nad sekcją stoi informacja „Te funkcje są niestabilne”.
  6. Jeśli wpływa na czystą logikę, dodaj test.

Nowe klucze są automatycznie uwzględniane przez eksport/import i plik konfiguracyjny — oba mechanizmy przechodzą przez całą przestrzeń nazw Switcher.*, zamiast korzystać z ręcznie przygotowanej listy.

Dokumentacja konfiguracji na tej stronie jest generowana z kopii tego samego pliku schema.json. Po dodaniu preferencji odśwież ją, aby dokumentacja nie pozostawała w tyle — zobacz docs/README.md.

Lista kontrolna pull requestu

  • Projekt buduje się bez błędów i nowych ostrzeżeń.
  • Istniejące testy nadal przechodzą; nowa funkcja z czystą logiką zawiera co najmniej jeden test.
  • Bez zakomentowanego kodu, martwych gałęzi i pozostawionego print — logowanie przez Log.* (os.Logger).
  • Jeden logiczny zakres zmian na PR. Refaktoryzacje oddziel od zmian zachowania.
  • Komunikaty commitów mają postać type: short summaryfix:, feat:, perf:, refactor:, docs:, chore: — a treść wyjaśniająca dlaczego jest zawijana do około 72 znaków.

Zgłaszanie błędu

Otwórz zgłoszenie i podaj:

  1. Wersję macOS (sw_vers)
  2. Wersję BetterCmdTab (pasek menu → Informacje)
  3. Kroki odtworzenia — dokładną sekwencję klawiszy, otwarte aplikacje i używany monitor
  4. Oczekiwane zachowanie oraz to, co faktycznie się wydarzyło
  5. Krótkie nagranie ekranu, jeśli problem jest wizualny (migotanie fokusu, układ, renderowanie szkła)

W przypadku awarii dołącz plik .ips z ~/Library/Logs/DiagnosticReports/.

Proponowanie funkcji

Opisz oczekiwany sposób pracy, a nie implementację. „Chcę filtrować przełącznik według kategorii aplikacji” jest bardziej użyteczne niż „dodaj listę rozwijaną kategorii” — implementację można przedyskutować, ale to podstawowa potrzeba kieruje projektem.

Bezpieczeństwo

Jeśli znajdziesz lukę — cokolwiek, co pozwala zewnętrznej aplikacji odczytywać stan przełącznika, przechwytywać skróty lub zwiększać uprawnienia za pomocą Dostępności przyznanej BetterCmdTab — otwórz prywatne zgłoszenie bezpieczeństwa, a nie publiczne zgłoszenie.

Licencja

BetterCmdTab jest objęty licencją GPL v3. Wysyłając swój wkład, zgadzasz się, że Twoja praca również jest objęta tą licencją.

Na tej stronie