macos: add option to prompt user for confirmation on OSC 52 commands

This commit is contained in:
Gregory Anders
2023-11-09 20:45:50 -06:00
parent ace5693957
commit 86245ff0cf
16 changed files with 362 additions and 188 deletions

View File

@@ -40,9 +40,9 @@
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
A5E112932AF73E6E00C6E0C2 /* PasteProtection.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* PasteProtection.xib */; };
A5E112952AF73E8A00C6E0C2 /* PasteProtectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* PasteProtectionController.swift */; };
A5E112972AF7401B00C6E0C2 /* PasteProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* PasteProtectionView.swift */; };
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */; };
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */; };
A5FEB3002ABB69450068369E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FEB2FF2ABB69450068369E /* main.swift */; };
/* End PBXBuildFile section */
@@ -83,9 +83,9 @@
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
A5E112922AF73E6E00C6E0C2 /* PasteProtection.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PasteProtection.xib; sourceTree = "<group>"; };
A5E112942AF73E8A00C6E0C2 /* PasteProtectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteProtectionController.swift; sourceTree = "<group>"; };
A5E112962AF7401B00C6E0C2 /* PasteProtectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteProtectionView.swift; sourceTree = "<group>"; };
A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClipboardConfirmation.xib; sourceTree = "<group>"; };
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationView.swift; sourceTree = "<group>"; };
A5FEB2FF2ABB69450068369E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -107,7 +107,7 @@
children = (
A56D58872ACDE6BE00508D2C /* Services */,
A59630982AEE1C4400D64628 /* Terminal */,
A5E112912AF73E4D00C6E0C2 /* Paste Protection */,
A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */,
A534263E2A7DCC5800EBB7A2 /* Settings */,
);
path = Features;
@@ -234,14 +234,14 @@
name = Frameworks;
sourceTree = "<group>";
};
A5E112912AF73E4D00C6E0C2 /* Paste Protection */ = {
A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */ = {
isa = PBXGroup;
children = (
A5E112922AF73E6E00C6E0C2 /* PasteProtection.xib */,
A5E112942AF73E8A00C6E0C2 /* PasteProtectionController.swift */,
A5E112962AF7401B00C6E0C2 /* PasteProtectionView.swift */,
A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */,
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */,
A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */,
);
path = "Paste Protection";
path = ClipboardConfirmation;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -306,7 +306,7 @@
A596309A2AEE1C6400D64628 /* Terminal.xib in Resources */,
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */,
A5E112932AF73E6E00C6E0C2 /* PasteProtection.xib in Resources */,
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */,
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */,
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */,
);
@@ -342,11 +342,11 @@
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */,
A5E112952AF73E8A00C6E0C2 /* PasteProtectionController.swift in Sources */,
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */,
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */,
A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */,
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,
A5E112972AF7401B00C6E0C2 /* PasteProtectionView.swift in Sources */,
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -13,11 +13,11 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Warning: Potentially Unsafe Paste" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<window title="Clipboard Confirmation" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5" userLabel="Clipboard Confirmation">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
<rect key="screenRect" x="0.0" y="0.0" width="1512" height="944"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>

View File

@@ -0,0 +1,49 @@
import Foundation
import Cocoa
import SwiftUI
import GhosttyKit
/// This initializes a clipboard confirmation warning window. The window itself
/// WILL NOT show automatically and the caller must show the window via
/// showWindow, beginSheet, etc.
class ClipboardConfirmationController: NSWindowController {
override var windowNibName: NSNib.Name? { "ClipboardConfirmation" }
let surface: ghostty_surface_t
let contents: String
let reason: Ghostty.ClipboardPromptReason
let state: UnsafeMutableRawPointer?
weak private var delegate: ClipboardConfirmationViewDelegate? = nil
init(surface: ghostty_surface_t, contents: String, reason: Ghostty.ClipboardPromptReason, state: UnsafeMutableRawPointer?, delegate: ClipboardConfirmationViewDelegate) {
self.surface = surface
self.contents = contents
self.reason = reason
self.state = state
self.delegate = delegate
super.init(window: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported for this view")
}
//MARK: - NSWindowController
override func windowDidLoad() {
guard let window = window else { return }
switch (reason) {
case .unsafe:
window.title = "Warning: Potentially Unsafe Paste"
case .read, .write:
window.title = "Authorize Clipboard Access"
}
window.contentView = NSHostingView(rootView: ClipboardConfirmationView(
contents: contents,
reason: reason,
delegate: delegate
))
}
}

View File

@@ -0,0 +1,77 @@
import SwiftUI
/// This delegate is notified of the completion result of the clipboard confirmation dialog.
protocol ClipboardConfirmationViewDelegate: AnyObject {
func clipboardConfirmationComplete(_ action: ClipboardConfirmationView.Action, _ reason: Ghostty.ClipboardPromptReason)
}
/// The SwiftUI view for showing a clipboard confirmation dialog.
struct ClipboardConfirmationView: View {
enum Action : String {
case cancel
case confirm
static func text(_ action: Action, _ reason: Ghostty.ClipboardPromptReason) -> String {
switch (action) {
case .cancel:
switch (reason) {
case .unsafe: return "Cancel"
case .read, .write: return "Deny"
}
case .confirm:
switch (reason) {
case .unsafe: return "Paste"
case .read, .write: return "Allow"
}
}
}
}
/// The contents of the paste.
let contents: String
/// The reason for displaying the view
let reason: Ghostty.ClipboardPromptReason
/// Optional delegate to get results. If this is nil, then this view will never close on its own.
weak var delegate: ClipboardConfirmationViewDelegate? = nil
var body: some View {
VStack {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.yellow)
.font(.system(size: 42))
.padding()
.frame(alignment: .center)
Text(reason.text())
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
TextEditor(text: .constant(contents))
.textSelection(.enabled)
.font(.system(.body, design: .monospaced))
.padding(.all, 4)
HStack {
Spacer()
Button(Action.text(.cancel, reason)) { onCancel() }
.keyboardShortcut(.cancelAction)
Button(Action.text(.confirm, reason)) { onPaste() }
.keyboardShortcut(.defaultAction)
Spacer()
}
.padding(.bottom)
}
}
private func onCancel() {
delegate?.clipboardConfirmationComplete(.cancel, reason)
}
private func onPaste() {
delegate?.clipboardConfirmationComplete(.confirm, reason)
}
}

View File

@@ -1,37 +0,0 @@
import Foundation
import Cocoa
import SwiftUI
import GhosttyKit
/// This initializes an "unsafe paste" warning window. The window itself WILL NOT show automatically
/// and the caller must show the window via showWindow, beginSheet, etc.
class PasteProtectionController: NSWindowController {
override var windowNibName: NSNib.Name? { "PasteProtection" }
let surface: ghostty_surface_t
let contents: String
let state: UnsafeMutableRawPointer?
weak private var delegate: PasteProtectionViewDelegate? = nil
init(surface: ghostty_surface_t, contents: String, state: UnsafeMutableRawPointer?, delegate: PasteProtectionViewDelegate) {
self.surface = surface
self.contents = contents
self.state = state
self.delegate = delegate
super.init(window: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported for this view")
}
//MARK: - NSWindowController
override func windowDidLoad() {
guard let window = window else { return }
window.contentView = NSHostingView(rootView: PasteProtectionView(
contents: contents,
delegate: delegate
))
}
}

View File

@@ -1,60 +0,0 @@
import SwiftUI
/// This delegate is notified of the completion result of the paste protection dialog.
protocol PasteProtectionViewDelegate: AnyObject {
func pasteProtectionComplete(_ action: PasteProtectionView.Action)
}
/// The SwiftUI view for showing a paste protection dialog.
struct PasteProtectionView: View {
enum Action : String {
case cancel
case paste
}
/// The contents of the paste.
let contents: String
/// Optional delegate to get results. If this is nil, then this view will never close on its own.
weak var delegate: PasteProtectionViewDelegate? = nil
var body: some View {
VStack {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.yellow)
.font(.system(size: 42))
.padding()
.frame(alignment: .center)
Text("Pasting this text to the terminal may be dangerous as it looks like " +
"some commands may be executed.")
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
TextEditor(text: .constant(contents))
.textSelection(.enabled)
.font(.system(.body, design: .monospaced))
.padding(.all, 4)
HStack {
Spacer()
Button("Cancel") { onCancel() }
.keyboardShortcut(.cancelAction)
Button("Paste") { onPaste() }
.keyboardShortcut(.defaultAction)
Spacer()
}
.padding(.bottom)
}
}
private func onCancel() {
delegate?.pasteProtectionComplete(.cancel)
}
private func onPaste() {
delegate?.pasteProtectionComplete(.paste)
}
}

View File

@@ -6,7 +6,7 @@ import GhosttyKit
/// The terminal controller is an NSWindowController that maps 1:1 to a terminal window.
class TerminalController: NSWindowController, NSWindowDelegate,
TerminalViewDelegate, TerminalViewModel,
PasteProtectionViewDelegate
ClipboardConfirmationViewDelegate
{
override var windowNibName: NSNib.Name? { "Terminal" }
@@ -31,8 +31,8 @@ class TerminalController: NSWindowController, NSWindowDelegate,
/// True when an alert is active so we don't overlap multiple.
private var alert: NSAlert? = nil
/// The paste protection window, if shown.
private var pasteProtection: PasteProtectionController? = nil
/// The clipboard confirmation window, if shown.
private var clipboardConfirmation: ClipboardConfirmationController? = nil
init(_ ghostty: Ghostty.AppState, withBaseConfig base: Ghostty.SurfaceConfiguration? = nil) {
self.ghostty = ghostty
@@ -56,8 +56,8 @@ class TerminalController: NSWindowController, NSWindowDelegate,
object: nil)
center.addObserver(
self,
selector: #selector(onConfirmUnsafePaste),
name: Ghostty.Notification.confirmUnsafePaste,
selector: #selector(onConfirmClipboardRequest),
name: Ghostty.Notification.confirmClipboard,
object: nil)
}
@@ -346,28 +346,36 @@ class TerminalController: NSWindowController, NSWindowDelegate,
self.window?.close()
}
//MARK: - Paste Protection
//MARK: - Clipboard Confirmation
func pasteProtectionComplete(_ action: PasteProtectionView.Action) {
// End our paste protection no matter what
guard let pp = self.pasteProtection else { return }
self.pasteProtection = nil
func clipboardConfirmationComplete(_ action: ClipboardConfirmationView.Action, _ reason: Ghostty.ClipboardPromptReason) {
// End our clipboard confirmation no matter what
guard let cc = self.clipboardConfirmation else { return }
self.clipboardConfirmation = nil
// Close the sheet
if let ppWindow = pp.window {
window?.endSheet(ppWindow)
if let ccWindow = cc.window {
window?.endSheet(ccWindow)
}
let str: String
switch (action) {
case .cancel:
str = ""
case .paste:
str = pp.contents
switch (reason) {
case .write:
guard case .confirm = action else { break }
let pb = NSPasteboard.general
pb.declareTypes([.string], owner: nil)
pb.setString(cc.contents, forType: .string)
case .read, .unsafe:
let str: String
switch (action) {
case .cancel:
str = ""
case .confirm:
str = cc.contents
}
Ghostty.AppState.completeClipboardRequest(cc.surface, data: str, state: cc.state, confirmed: true)
}
Ghostty.AppState.completeClipboardRequest(pp.surface, data: str, state: pp.state, confirmed: true)
}
//MARK: - Notifications
@@ -429,7 +437,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
}
}
@objc private func onConfirmUnsafePaste(notification: SwiftUI.Notification) {
@objc private func onConfirmClipboardRequest(notification: SwiftUI.Notification) {
guard let target = notification.object as? Ghostty.SurfaceView else { return }
guard target == self.focusedSurface else { return }
guard let surface = target.surface else { return }
@@ -438,23 +446,25 @@ class TerminalController: NSWindowController, NSWindowDelegate,
guard let window = self.window else { return }
// Check whether we use non-native fullscreen
guard let str = notification.userInfo?[Ghostty.Notification.UnsafePasteStrKey] as? String else { return }
guard let state = notification.userInfo?[Ghostty.Notification.UnsafePasteStateKey] as? UnsafeMutableRawPointer? else { return }
guard let str = notification.userInfo?[Ghostty.Notification.ConfirmClipboardStrKey] as? String else { return }
guard let state = notification.userInfo?[Ghostty.Notification.ConfirmClipboardStateKey] as? UnsafeMutableRawPointer? else { return }
guard let reason = notification.userInfo?[Ghostty.Notification.ConfirmClipboardReasonKey] as? Ghostty.ClipboardPromptReason else { return }
// If we already have a paste protection view up, we ignore this request.
// If we already have a clipboard confirmation view up, we ignore this request.
// This shouldn't be possible...
guard self.pasteProtection == nil else {
guard self.clipboardConfirmation == nil else {
Ghostty.AppState.completeClipboardRequest(surface, data: "", state: state, confirmed: true)
return
}
// Show our paste confirmation
self.pasteProtection = PasteProtectionController(
self.clipboardConfirmation = ClipboardConfirmationController(
surface: surface,
contents: str,
reason: reason,
state: state,
delegate: self
)
window.beginSheet(self.pasteProtection!.window!)
window.beginSheet(self.clipboardConfirmation!.window!)
}
}

View File

@@ -150,8 +150,8 @@ extension Ghostty {
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
set_mouse_visibility_cb: { userdata, visible in AppState.setMouseVisibility(userdata, visible: visible) },
read_clipboard_cb: { userdata, loc, state in AppState.readClipboard(userdata, location: loc, state: state) },
confirm_read_clipboard_cb: { userdata, str, state in AppState.confirmReadClipboard(userdata, string: str, state: state ) },
write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) },
confirm_read_clipboard_cb: { userdata, str, state, reason in AppState.confirmReadClipboard(userdata, string: str, state: state, reason: reason ) },
write_clipboard_cb: { userdata, str, loc, confirm in AppState.writeClipboard(userdata, string: str, location: loc, confirm: confirm) },
new_split_cb: { userdata, direction, surfaceConfig in AppState.newSplit(userdata, direction: direction, config: surfaceConfig) },
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
new_window_cb: { userdata, surfaceConfig in AppState.newWindow(userdata, config: surfaceConfig) },
@@ -433,16 +433,19 @@ extension Ghostty {
static func confirmReadClipboard(
_ userdata: UnsafeMutableRawPointer?,
string: UnsafePointer<CChar>?,
state: UnsafeMutableRawPointer?
state: UnsafeMutableRawPointer?,
reason: ghostty_clipboard_prompt_reason_e
) {
guard let surface = self.surfaceUserdata(from: userdata) else { return }
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
guard let reason = Ghostty.ClipboardPromptReason.from(reason: reason) else { return }
NotificationCenter.default.post(
name: Notification.confirmUnsafePaste,
name: Notification.confirmClipboard,
object: surface,
userInfo: [
Notification.UnsafePasteStrKey: valueStr,
Notification.UnsafePasteStateKey: state as Any
Notification.ConfirmClipboardStrKey: valueStr,
Notification.ConfirmClipboardStateKey: state as Any,
Notification.ConfirmClipboardReasonKey: reason,
]
)
}
@@ -458,14 +461,27 @@ extension Ghostty {
}
}
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e) {
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
guard let surface = self.surfaceUserdata(from: userdata) else { return }
// We only support the standard clipboard
if (location != GHOSTTY_CLIPBOARD_STANDARD) { return }
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
let pb = NSPasteboard.general
pb.declareTypes([.string], owner: nil)
pb.setString(valueStr, forType: .string)
if !confirm {
let pb = NSPasteboard.general
pb.declareTypes([.string], owner: nil)
pb.setString(valueStr, forType: .string)
} else {
NotificationCenter.default.post(
name: Notification.confirmClipboard,
object: surface,
userInfo: [
Notification.ConfirmClipboardStrKey: valueStr,
Notification.ConfirmClipboardReasonKey: Ghostty.ClipboardPromptReason.write,
]
)
}
}
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {

View File

@@ -94,6 +94,61 @@ extension Ghostty {
}
}
}
/// The reason a clipboard prompt is shown to the user
enum ClipboardPromptReason {
/// An unsafe paste may cause commands to be executed
case unsafe
/// An application is attempting to read from the clipboard
case read
/// An applciation is attempting to write to the clipboard
case write
func text() -> String {
switch (self) {
case .unsafe:
return """
Pasting this text to the terminal may be dangerous as it looks like some commands may be executed.
"""
case .read:
return """
An application is attempting to read from the clipboard.
The current clipboard contents are shown below.
"""
case .write:
return """
An application is attempting to write to the clipboard.
The content to write is shown below.
"""
}
}
static func from(reason: ghostty_clipboard_prompt_reason_e) -> ClipboardPromptReason? {
switch (reason) {
case GHOSTTY_CLIPBOARD_PROMPT_UNSAFE:
return .unsafe
case GHOSTTY_CLIPBOARD_PROMPT_READ:
return .read
case GHOSTTY_CLIPBOARD_PROMPT_WRITE:
return .write
default:
return nil
}
}
func toNative() -> ghostty_clipboard_prompt_reason_e {
switch (self) {
case .unsafe:
return GHOSTTY_CLIPBOARD_PROMPT_UNSAFE
case .read:
return GHOSTTY_CLIPBOARD_PROMPT_READ
case .write:
return GHOSTTY_CLIPBOARD_PROMPT_WRITE
}
}
}
}
extension Ghostty.Notification {
@@ -142,9 +197,10 @@ extension Ghostty.Notification {
/// Notification to show/hide the inspector
static let didControlInspector = Notification.Name("com.mitchellh.ghostty.didControlInspector")
static let confirmUnsafePaste = Notification.Name("com.mitchellh.ghostty.confirmUnsafePaste")
static let UnsafePasteStrKey = confirmUnsafePaste.rawValue + ".str"
static let UnsafePasteStateKey = confirmUnsafePaste.rawValue + ".state"
static let confirmClipboard = Notification.Name("com.mitchellh.ghostty.confirmClipboard")
static let ConfirmClipboardStrKey = confirmClipboard.rawValue + ".str"
static let ConfirmClipboardStateKey = confirmClipboard.rawValue + ".state"
static let ConfirmClipboardReasonKey = confirmClipboard.rawValue + ".reason"
/// Notification sent to the active split view to resize the split.
static let didResizeSplit = Notification.Name("com.mitchellh.ghostty.didResizeSplit")