diff --git a/macos/Sources/Features/Primary Window/PrimaryView.swift b/macos/Sources/Features/Primary Window/PrimaryView.swift index da110011b..7d7515431 100644 --- a/macos/Sources/Features/Primary Window/PrimaryView.swift +++ b/macos/Sources/Features/Primary Window/PrimaryView.swift @@ -94,38 +94,46 @@ struct PrimaryView: View { self.appDelegate.confirmQuit = $0 }) - Ghostty.TerminalSplit(onClose: Self.closeWindow, baseConfig: self.baseConfig) - .ghosttyApp(ghostty.app!) - .ghosttyConfig(ghostty.config!) - .background(WindowAccessor(window: $window)) - .onReceive(gotoTab) { onGotoTab(notification: $0) } - .onReceive(toggleFullscreen) { onToggleFullscreen(notification: $0) } - .focused($focused) - .onAppear { self.focused = true } - .onChange(of: focusedSurface) { newValue in - self.focusedSurfaceWrapper.surface = newValue?.surface + VStack(spacing: 0) { + // If we're running in debug mode we show a warning so that users + // know that performance will be degraded. + if (ghostty.info.mode == GHOSTTY_BUILD_MODE_DEBUG) { + DebugBuildWarningView() } - .onChange(of: title) { newValue in - // We need to handle this manually because we are using AppKit lifecycle - // so navigationTitle no longer works. - guard let window = self.window else { return } - window.title = newValue - } - .confirmationDialog( - "Quit Ghostty?", - isPresented: confirmQuitting) { - Button("Close Ghostty") { - NSApplication.shared.reply(toApplicationShouldTerminate: true) - } - .keyboardShortcut(.defaultAction) - - Button("Cancel", role: .cancel) { - NSApplication.shared.reply(toApplicationShouldTerminate: false) - } - .keyboardShortcut(.cancelAction) - } message: { - Text("All terminal sessions will be terminated.") + + Ghostty.TerminalSplit(onClose: Self.closeWindow, baseConfig: self.baseConfig) + .ghosttyApp(ghostty.app!) + .ghosttyConfig(ghostty.config!) + .background(WindowAccessor(window: $window)) + .onReceive(gotoTab) { onGotoTab(notification: $0) } + .onReceive(toggleFullscreen) { onToggleFullscreen(notification: $0) } + .focused($focused) + .onAppear { self.focused = true } + .onChange(of: focusedSurface) { newValue in + self.focusedSurfaceWrapper.surface = newValue?.surface } + .onChange(of: title) { newValue in + // We need to handle this manually because we are using AppKit lifecycle + // so navigationTitle no longer works. + guard let window = self.window else { return } + window.title = newValue + } + .confirmationDialog( + "Quit Ghostty?", + isPresented: confirmQuitting) { + Button("Close Ghostty") { + NSApplication.shared.reply(toApplicationShouldTerminate: true) + } + .keyboardShortcut(.defaultAction) + + Button("Cancel", role: .cancel) { + NSApplication.shared.reply(toApplicationShouldTerminate: false) + } + .keyboardShortcut(.cancelAction) + } message: { + Text("All terminal sessions will be terminated.") + } + } } } @@ -196,3 +204,34 @@ struct PrimaryView: View { } } } + +struct DebugBuildWarningView: View { + @State private var isPopover = false + + var body: some View { + HStack { + Spacer() + + Image(systemName: "exclamationmark.triangle.fill") + .foregroundColor(.yellow) + + Text("You're running a debug build of Ghostty!") + .padding(.all, 8) + .popover(isPresented: $isPopover, arrowEdge: .bottom) { + Text(""" + Debug builds of Ghostty are very slow and you may experience + performance problems. Debug builds are only recommended during + development. + """) + .padding(.all) + } + + Spacer() + } + .background(Color(.windowBackgroundColor)) + .frame(maxWidth: .infinity) + .onTapGesture { + isPopover = true + } + } +} diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index fda9bf9b5..253c705f8 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -11,6 +11,11 @@ extension Ghostty { case loading, error, ready } + struct Info { + var mode: ghostty_build_mode_e + var version: String + } + /// The AppState is the global state that is associated with the Swift app. This handles initially /// initializing Ghostty, loading the configuration, etc. class AppState: ObservableObject { @@ -46,6 +51,18 @@ extension Ghostty { return ghostty_app_needs_confirm_quit(app) } + /// Build information + var info: Info { + let raw = ghostty_info() + let version = NSString( + bytes: raw.version, + length: Int(raw.version_len), + encoding: NSUTF8StringEncoding + ) ?? "unknown" + + return Info(mode: raw.build_mode, version: String(version)) + } + /// Cached clipboard string for `read_clipboard` callback. private var cached_clipboard_string: String? = nil