Files
ghostty-rs/docs/rewrite/notes/macos-app-MA1.md
T
ddidderr 4861015105 feat(macos): foundation of the macOS app ported from Swift to Rust
Phase 9, MA1: crates/ghostty-macos (AppKit via objc2; empty stub on
non-Apple targets) ports the foundation of Ghostty 1.3.1's Swift macOS
app (ghostty/macos): main, AppDelegate (+Ghostty) with lifecycle, dock
menu, open-file, notifications, shortcut sync and quit/reopen paths;
MainMenu.xib as generated menu tables built into NSMenus; the
Ghostty.App/Config/Surface wrappers driving libghostty in-process via
ghostty-lib; the generic helpers; scripts/macos-bundle.sh to assemble
Ghostty.app (dry-run on Linux). Feature areas are stubbed for MA2-MA5.
Design and type mapping: docs/rewrite/notes/macos-app-design.md
(App Intents is a Swift-only framework and cannot be ported).

Compile-checked only (no macOS here): just check-macos passes for
aarch64/x86_64-apple-darwin with -D warnings; 125 platform-independent
tests run on Linux.

Test Plan:
- just check-macos
- cargo test -p ghostty-macos
2026-10-12 19:25:44 +02:00

46 lines
3.8 KiB
Markdown

# macOS app, MA1: foundation
Design and work split: `macos-app-design.md`. Crate `crates/ghostty-macos` (lib `ghostty_macos`, bin `ghostty-macos-app`;
empty stub off Apple). Compile-checked only (`just check-macos`, aarch64 + x86_64, clippy `-D warnings`, lib + tests + bin);
never run on a Mac. Platform independent logic is unit-tested on Linux (125+ tests).
## Ported (Swift -> Rust)
* `App/macOS/main.swift` -> `app/entry.rs`; `AppDelegate.swift` -> `app/app_delegate.rs` (all delegate methods, lifecycle,
dock menu, open-file with execute confirmation, quit confirmation incl. shutdown/restart AppleEvent check, SIGUSR2 reload,
local key monitor, bell/dock badge, user notifications via objc2-user-notifications, appearance KVO, config-change handling,
menu shortcut sync + `performGhosttyBindingMenuKeyEquivalent`, float-on-top, set-as-default-terminal, `validateMenuItem`,
toggle visibility); `AppDelegate+Ghostty.swift` -> `app/app_delegate_ghostty.rs`.
* `MainMenu.xib` -> `app/menu_spec/` (tables generated by `scripts/gen-macos-menu-spec.py`, plus `Outlet`, `SYNCED_SHORTCUTS`,
`MENU_IMAGES`, `MenuShortcutKey`, dock menu; tested) and `app/main_menu.rs` (builds the NSMenus, system menus, NSFontManager).
* `Ghostty.App/Config/ConfigTypes/Command/Action/Error/Event/Shell/Surface`, `GhosttyPackage`, `NSEvent+Extension`, top of
`Ghostty.Input`, `SurfaceConfiguration`, a model-only `SurfaceView` -> `ghostty/*` (by forks F1-F3 of MA1; see design doc).
* `Helpers/*` generic ones -> `helpers/*`; unportable SwiftUI-only helpers listed in the design doc.
* `scripts/macos-bundle.sh` (Ghostty.app assembly, dry-run off macOS), `scripts/gen-macos-capi-consts.py`,
`scripts/gen-macos-menu-spec.py`, `scripts/macos-crosscheck.sh` now includes `ghostty-macos` (+ its bin).
* Stubs with the surface `AppDelegate`/`Ghostty.App` use (owners replace them): `features/terminal` (MA3), `quick_terminal::controller`,
`settings`, `about`, `secure_input`, `global_keybinds`, `services`, `custom_app_icon` (MA4), `update` (MA5).
## Deviations
* Menu outlets: the xib's `menuSelectionForSearch` outlet does not exist in Swift (and "Jump to Selection" is unconnected), so
those two items never get a config shortcut -- kept as upstream (module docs of `menu_spec`).
* `NSApplicationMain` + nib -> explicit NSApplication/delegate/menu setup in `entry::run` (no `NSMainNibFile`).
* `application(_:willEncode/didDecodeRestorableState:)`: quick terminal state not encoded yet (TODO(MA4)).
* `updateAppIcon`: AppIcon persistence is MA4's; only the distributed `iconDidChange` notification is posted.
* Fork-reported deviations: `Ghostty.Config.backgroundColor` falls back to a fixed light gray (Swift: dynamic
`windowBackgroundColor`); `window_fullscreen*` getters return raw strings until MA3 (typed `FullscreenMode`);
`open_config` passes the byte length of the URL (Swift `String.count` truncates non-ASCII paths); desktop notifications hop to the
main queue before touching the view; `CodableBridge` archives are not byte-compatible with Swift's; `ExpiringTarget` drop only
invalidates the timer; MIME->UTI lookups use CoreServices C functions; tab-add error is `ObjCException{name,reason}`;
`PasteboardHandle` is an identity `usize`; user-visible duration format "1 hr, 2 min, 3 sec" matches Foundation en_US
abbreviated style but the `.hide` fractional rounding is unverified off macOS.
## Known gaps / TODO(integration)
* Nothing here has run on a Mac; objc2 method signatures were only type-checked.
* `TODO(MA2)` in `ghostty/surface_view.rs`/`surface.rs`, `TODO(MA3)` terminal stubs and `fullscreen::set_is_tab_bar_hook`,
`TODO(MA4)` feature stubs, `TODO(MA5)` update.
* `SurfaceView::set_password_input` does not yet drive `SecureInput` (MA4).
* The bundle script does not embed Sparkle/the DockTilePlugin (MA5/MA4); no `.lproj` localizations exist in the Swift tree either.