4 Commits

536 changed files with 18944 additions and 296 deletions

View File

@@ -16,19 +16,24 @@
1AB4A0F1BE668D3130EFBA93 /* TerminalTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */; }; 1AB4A0F1BE668D3130EFBA93 /* TerminalTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */; };
2089566A2BBAA65EA82119B3 /* NotchOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6D136A0B3FC79DDE12A826 /* NotchOrchestratorTests.swift */; }; 2089566A2BBAA65EA82119B3 /* NotchOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6D136A0B3FC79DDE12A826 /* NotchOrchestratorTests.swift */; };
21373D6E9C2F34FD63CEC6A5 /* TerminalScrollCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D9E323185F6D722B360C3C /* TerminalScrollCoordinatorTests.swift */; }; 21373D6E9C2F34FD63CEC6A5 /* TerminalScrollCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D9E323185F6D722B360C3C /* TerminalScrollCoordinatorTests.swift */; };
229567E62C617B045E92E0FE /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 1375B54FF94D210B46F18540 /* ghostty */; };
2375B9DA559A0777FE558A8B /* WorkspaceStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */; }; 2375B9DA559A0777FE558A8B /* WorkspaceStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */; };
23E2DDCF36D0DAB2EA72C39C /* NotchState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */; }; 23E2DDCF36D0DAB2EA72C39C /* NotchState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */; };
26AE379040149CBE05B314BB /* WorkspacesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A31EFACF23DD9262A040E /* WorkspacesSettingsView.swift */; }; 26AE379040149CBE05B314BB /* WorkspacesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A31EFACF23DD9262A040E /* WorkspacesSettingsView.swift */; };
278010607B0D552DCC8996C5 /* CommandNotchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */; }; 278010607B0D552DCC8996C5 /* CommandNotchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */; };
28F609FCF422E735F567EE32 /* SwiftTermBackendSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3942F3843D0A13C145576760 /* SwiftTermBackendSession.swift */; };
2DF22798D3A7514E2A9183FC /* WorkspaceSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */; }; 2DF22798D3A7514E2A9183FC /* WorkspaceSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */; };
34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */; }; 34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */; };
3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */; }; 3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */; };
3DC413CDD97DF0233F039518 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D58535EA23D3C2A5D0520DB3 /* Carbon.framework */; };
40183737D8C237022D0882FD /* TerminalScrollbackEstimatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */; }; 40183737D8C237022D0882FD /* TerminalScrollbackEstimatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */; };
4BA9D6274CFD6A87DC397B8E /* MouseAwareTerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982BA8965FE92A59D66F378B /* MouseAwareTerminalView.swift */; };
4CFA0C79095ACE0A327A2469 /* WorkspaceRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */; }; 4CFA0C79095ACE0A327A2469 /* WorkspaceRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */; };
4D335F67B71F7DD977B6AEF9 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C98C406899F4B242075AF /* AppSettingsStore.swift */; }; 4D335F67B71F7DD977B6AEF9 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C98C406899F4B242075AF /* AppSettingsStore.swift */; };
4E0AD14B7427532271E485AA /* NotchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */; }; 4E0AD14B7427532271E485AA /* NotchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */; };
507A67E770DEFAF5BC321FCF /* WindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9220E02B6D470DD05CA540C /* WindowCoordinator.swift */; }; 507A67E770DEFAF5BC321FCF /* WindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9220E02B6D470DD05CA540C /* WindowCoordinator.swift */; };
5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = C921E6435A64AA07A0FEA4D5 /* SwiftTerm */; }; 5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = C921E6435A64AA07A0FEA4D5 /* SwiftTerm */; };
567914B84D0EEF6E6E9149A9 /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = 3A03006228968FE12FCBF588 /* terminfo */; };
5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */; }; 5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */; };
5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */; }; 5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */; };
65C7DB7296C6C6A77598A1F4 /* TerminalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37A6DCD9C5DE1FE11C4C1CD /* TerminalSettingsView.swift */; }; 65C7DB7296C6C6A77598A1F4 /* TerminalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37A6DCD9C5DE1FE11C4C1CD /* TerminalSettingsView.swift */; };
@@ -46,6 +51,7 @@
88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A1D3D12BAC593838B3125C /* TabBar.swift */; }; 88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A1D3D12BAC593838B3125C /* TabBar.swift */; };
88D87155D79C493D8956AA3B /* AnimationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE99DBAB656EA80A117D2EE1 /* AnimationSettingsView.swift */; }; 88D87155D79C493D8956AA3B /* AnimationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE99DBAB656EA80A117D2EE1 /* AnimationSettingsView.swift */; };
8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC7F7D8D15A1BC4EE43DDDB /* ScreenRegistryTests.swift */; }; 8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC7F7D8D15A1BC4EE43DDDB /* ScreenRegistryTests.swift */; };
99262FD443753BA518221CBD /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB445256B2E0228F7679EE7 /* GhosttyKit.xcframework */; };
9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */; }; 9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */; };
A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDC7451F0E7ACFE0BEC5473 /* NSScreen+Extensions.swift */; }; A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDC7451F0E7ACFE0BEC5473 /* NSScreen+Extensions.swift */; };
AE8215538D4B026A00BAD241 /* WorkspaceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B957BD1F6120D2592613ED /* WorkspaceStore.swift */; }; AE8215538D4B026A00BAD241 /* WorkspaceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B957BD1F6120D2592613ED /* WorkspaceStore.swift */; };
@@ -56,6 +62,7 @@
D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */; }; D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */; };
D2468B19D6F0A2C1DFDFE2B7 /* ScreenContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A770A63582CF9834F4E7F058 /* ScreenContextTests.swift */; }; D2468B19D6F0A2C1DFDFE2B7 /* ScreenContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A770A63582CF9834F4E7F058 /* ScreenContextTests.swift */; };
D4CEB4B895F75D91FA892A06 /* PopoutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB593A2546BF2C0BE8E40387 /* PopoutWindowController.swift */; }; D4CEB4B895F75D91FA892A06 /* PopoutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB593A2546BF2C0BE8E40387 /* PopoutWindowController.swift */; };
D6BBC4DE23DEA39D9713469A /* TerminalScrollWheelRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7315EA485A52A8DC42DF925E /* TerminalScrollWheelRouterTests.swift */; };
DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3125FD3DC55420122CF85D80 /* SettingsView.swift */; }; DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3125FD3DC55420122CF85D80 /* SettingsView.swift */; };
DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22AA47452CF798A977A6F47 /* TerminalCommandArrowBehavior.swift */; }; DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22AA47452CF798A977A6F47 /* TerminalCommandArrowBehavior.swift */; };
DD116B0FCC341D66F1534EC4 /* TerminalScrollbackEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */; }; DD116B0FCC341D66F1534EC4 /* TerminalScrollbackEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */; };
@@ -64,6 +71,7 @@
EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */; }; EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */; };
F8ED522FDC96B1F9AD8933F7 /* ScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */; }; F8ED522FDC96B1F9AD8933F7 /* ScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */; };
F8F8DE2F26608259D72635B7 /* AppSettingsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */; }; F8F8DE2F26608259D72635B7 /* AppSettingsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */; };
FF6BB7E881BCBB4FD6CC4011 /* GhosttyBackendSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFC12727EA1C097E0EF9DF2 /* GhosttyBackendSession.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -85,13 +93,16 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistry.swift; sourceTree = "<group>"; }; 0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistry.swift; sourceTree = "<group>"; };
1375B54FF94D210B46F18540 /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = CommandNotch/Resources/ghostty; sourceTree = SOURCE_ROOT; };
165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScrollbackEstimator.swift; sourceTree = "<group>"; }; 165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScrollbackEstimator.swift; sourceTree = "<group>"; };
27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = "<group>"; }; 27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = "<group>"; };
297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSettings.swift; sourceTree = "<group>"; }; 297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSettings.swift; sourceTree = "<group>"; };
2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyRecorderView.swift; sourceTree = "<group>"; }; 2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyRecorderView.swift; sourceTree = "<group>"; };
3125FD3DC55420122CF85D80 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; }; 3125FD3DC55420122CF85D80 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
35CBC14E11EBD8486457CE91 /* CommandNotchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 35CBC14E11EBD8486457CE91 /* CommandNotchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3942F3843D0A13C145576760 /* SwiftTermBackendSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTermBackendSession.swift; sourceTree = "<group>"; };
39CA6F2C2CA32D0AA58F6C43 /* AppSettingsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStoreTests.swift; sourceTree = "<group>"; }; 39CA6F2C2CA32D0AA58F6C43 /* AppSettingsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStoreTests.swift; sourceTree = "<group>"; };
3A03006228968FE12FCBF588 /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = CommandNotch/Resources/terminfo; sourceTree = SOURCE_ROOT; };
3CB1DFD6FCDF64B4DF24230A /* WorkspaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceController.swift; sourceTree = "<group>"; }; 3CB1DFD6FCDF64B4DF24230A /* WorkspaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceController.swift; sourceTree = "<group>"; };
3F57837A7115DEEE11E14B40 /* NotchShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchShape.swift; sourceTree = "<group>"; }; 3F57837A7115DEEE11E14B40 /* NotchShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchShape.swift; sourceTree = "<group>"; };
3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsControllerTests.swift; sourceTree = "<group>"; }; 3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsControllerTests.swift; sourceTree = "<group>"; };
@@ -99,7 +110,9 @@
48198AFE5473B0F7AECAB3FB /* AboutSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSettingsView.swift; sourceTree = "<group>"; }; 48198AFE5473B0F7AECAB3FB /* AboutSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSettingsView.swift; sourceTree = "<group>"; };
496267F03E261FEC9EBD5A9D /* CommandNotchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 496267F03E261FEC9EBD5A9D /* CommandNotchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
49E1791BB45E1505500ACC67 /* TerminalSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSession.swift; sourceTree = "<group>"; }; 49E1791BB45E1505500ACC67 /* TerminalSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSession.swift; sourceTree = "<group>"; };
4AFC12727EA1C097E0EF9DF2 /* GhosttyBackendSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyBackendSession.swift; sourceTree = "<group>"; };
4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchState.swift; sourceTree = "<group>"; }; 4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchState.swift; sourceTree = "<group>"; };
4DB445256B2E0228F7679EE7 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GhosttyKit.xcframework; path = Vendor/GhosttyKit.xcframework; sourceTree = "<group>"; };
4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStoreTests.swift; sourceTree = "<group>"; }; 4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStoreTests.swift; sourceTree = "<group>"; };
5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchApp.swift; sourceTree = "<group>"; }; 5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchApp.swift; sourceTree = "<group>"; };
567E85A2ED628460CEC760DB /* TerminalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalManager.swift; sourceTree = "<group>"; }; 567E85A2ED628460CEC760DB /* TerminalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalManager.swift; sourceTree = "<group>"; };
@@ -110,6 +123,7 @@
726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchUITests.swift; sourceTree = "<group>"; }; 726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchUITests.swift; sourceTree = "<group>"; };
728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindowController.swift; sourceTree = "<group>"; }; 728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindowController.swift; sourceTree = "<group>"; };
72A1D3D12BAC593838B3125C /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; }; 72A1D3D12BAC593838B3125C /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
7315EA485A52A8DC42DF925E /* TerminalScrollWheelRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScrollWheelRouterTests.swift; sourceTree = "<group>"; };
74463E4EAB78F56345360CD5 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; }; 74463E4EAB78F56345360CD5 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSummary.swift; sourceTree = "<group>"; }; 7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSummary.swift; sourceTree = "<group>"; };
8210D7783A614FD7190F5DDD /* LaunchAtLoginHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLoginHelper.swift; sourceTree = "<group>"; }; 8210D7783A614FD7190F5DDD /* LaunchAtLoginHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLoginHelper.swift; sourceTree = "<group>"; };
@@ -118,6 +132,7 @@
8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyBinding.swift; sourceTree = "<group>"; }; 8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyBinding.swift; sourceTree = "<group>"; };
8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenManager.swift; sourceTree = "<group>"; }; 8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenManager.swift; sourceTree = "<group>"; };
900F0476BE9E3600FBD371BB /* SettingsBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBindings.swift; sourceTree = "<group>"; }; 900F0476BE9E3600FBD371BB /* SettingsBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBindings.swift; sourceTree = "<group>"; };
982BA8965FE92A59D66F378B /* MouseAwareTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseAwareTerminalView.swift; sourceTree = "<group>"; };
9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTermView.swift; sourceTree = "<group>"; }; 9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTermView.swift; sourceTree = "<group>"; };
9E6C98C406899F4B242075AF /* AppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStore.swift; sourceTree = "<group>"; }; 9E6C98C406899F4B242075AF /* AppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStore.swift; sourceTree = "<group>"; };
A64A11F27E65B342B991629A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; A64A11F27E65B342B991629A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -129,6 +144,7 @@
CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalTheme.swift; sourceTree = "<group>"; }; CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalTheme.swift; sourceTree = "<group>"; };
D03D042117E59DCA9D553844 /* HotkeySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeySettingsView.swift; sourceTree = "<group>"; }; D03D042117E59DCA9D553844 /* HotkeySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeySettingsView.swift; sourceTree = "<group>"; };
D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFrameCalculatorTests.swift; sourceTree = "<group>"; }; D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFrameCalculatorTests.swift; sourceTree = "<group>"; };
D58535EA23D3C2A5D0520DB3 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScrollbackEstimatorTests.swift; sourceTree = "<group>"; }; D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScrollbackEstimatorTests.swift; sourceTree = "<group>"; };
DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestrator.swift; sourceTree = "<group>"; }; DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestrator.swift; sourceTree = "<group>"; };
DF0FFBC96F2446687D6474F4 /* WorkspaceSwitcherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSwitcherView.swift; sourceTree = "<group>"; }; DF0FFBC96F2446687D6474F4 /* WorkspaceSwitcherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSwitcherView.swift; sourceTree = "<group>"; };
@@ -152,6 +168,8 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */, 5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */,
99262FD443753BA518221CBD /* GhosttyKit.xcframework in Frameworks */,
3DC413CDD97DF0233F039518 /* Carbon.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -164,12 +182,14 @@
74463E4EAB78F56345360CD5 /* AppSettings.swift */, 74463E4EAB78F56345360CD5 /* AppSettings.swift */,
EB28950392C0198E69F3564B /* AppSettingsController.swift */, EB28950392C0198E69F3564B /* AppSettingsController.swift */,
9E6C98C406899F4B242075AF /* AppSettingsStore.swift */, 9E6C98C406899F4B242075AF /* AppSettingsStore.swift */,
4AFC12727EA1C097E0EF9DF2 /* GhosttyBackendSession.swift */,
8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */, 8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */,
DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */, DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */,
297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */, 297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */,
4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */, 4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */,
7181BB1F3926B457445105E5 /* ScreenContext.swift */, 7181BB1F3926B457445105E5 /* ScreenContext.swift */,
AAF23753B5A0CAF04D7566A3 /* ScreenRegistry.swift */, AAF23753B5A0CAF04D7566A3 /* ScreenRegistry.swift */,
3942F3843D0A13C145576760 /* SwiftTermBackendSession.swift */,
567E85A2ED628460CEC760DB /* TerminalManager.swift */, 567E85A2ED628460CEC760DB /* TerminalManager.swift */,
165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */, 165DCCD7BB164A6470D49BBF /* TerminalScrollbackEstimator.swift */,
49E1791BB45E1505500ACC67 /* TerminalSession.swift */, 49E1791BB45E1505500ACC67 /* TerminalSession.swift */,
@@ -182,6 +202,15 @@
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
19C217A7F1FCA6AE26E2FC4E /* Frameworks */ = {
isa = PBXGroup;
children = (
D58535EA23D3C2A5D0520DB3 /* Carbon.framework */,
4DB445256B2E0228F7679EE7 /* GhosttyKit.xcframework */,
);
name = Frameworks;
sourceTree = "<group>";
};
618799FE544A4373B457DCDA /* Extensions */ = { 618799FE544A4373B457DCDA /* Extensions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -193,9 +222,12 @@
7043235A31A4023478DA1302 = { 7043235A31A4023478DA1302 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1375B54FF94D210B46F18540 /* ghostty */,
3A03006228968FE12FCBF588 /* terminfo */,
84740FA9CF6A18B35EC82623 /* CommandNotch */, 84740FA9CF6A18B35EC82623 /* CommandNotch */,
A2F9603AB9C86C4EA62FFA59 /* CommandNotchTests */, A2F9603AB9C86C4EA62FFA59 /* CommandNotchTests */,
E6C841E864A0CC68B9B05BAC /* CommandNotchUITests */, E6C841E864A0CC68B9B05BAC /* CommandNotchUITests */,
19C217A7F1FCA6AE26E2FC4E /* Frameworks */,
B269158E04E8E603B61448F0 /* Products */, B269158E04E8E603B61448F0 /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
@@ -227,6 +259,7 @@
C0D19729317029008D81F361 /* TerminalCommandArrowBehaviorTests.swift */, C0D19729317029008D81F361 /* TerminalCommandArrowBehaviorTests.swift */,
D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */, D77DC2AED643512C786AD70A /* TerminalScrollbackEstimatorTests.swift */,
40D9E323185F6D722B360C3C /* TerminalScrollCoordinatorTests.swift */, 40D9E323185F6D722B360C3C /* TerminalScrollCoordinatorTests.swift */,
7315EA485A52A8DC42DF925E /* TerminalScrollWheelRouterTests.swift */,
D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */, D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */,
591FCE91AF83A8A8E44E1625 /* WorkspaceRegistryTests.swift */, 591FCE91AF83A8A8E44E1625 /* WorkspaceRegistryTests.swift */,
4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */, 4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */,
@@ -257,6 +290,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */, 2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */,
982BA8965FE92A59D66F378B /* MouseAwareTerminalView.swift */,
3F57837A7115DEEE11E14B40 /* NotchShape.swift */, 3F57837A7115DEEE11E14B40 /* NotchShape.swift */,
EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */, EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */,
9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */, 9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */,
@@ -408,6 +442,8 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7AB14019B5CC6ED84B96FA47 /* Assets.xcassets in Resources */, 7AB14019B5CC6ED84B96FA47 /* Assets.xcassets in Resources */,
229567E62C617B045E92E0FE /* ghostty in Resources */,
567914B84D0EEF6E6E9149A9 /* terminfo in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -433,6 +469,7 @@
8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */, 8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */,
CFBBE994BA6BAD4658AAB9CB /* TerminalCommandArrowBehaviorTests.swift in Sources */, CFBBE994BA6BAD4658AAB9CB /* TerminalCommandArrowBehaviorTests.swift in Sources */,
21373D6E9C2F34FD63CEC6A5 /* TerminalScrollCoordinatorTests.swift in Sources */, 21373D6E9C2F34FD63CEC6A5 /* TerminalScrollCoordinatorTests.swift in Sources */,
D6BBC4DE23DEA39D9713469A /* TerminalScrollWheelRouterTests.swift in Sources */,
40183737D8C237022D0882FD /* TerminalScrollbackEstimatorTests.swift in Sources */, 40183737D8C237022D0882FD /* TerminalScrollbackEstimatorTests.swift in Sources */,
0F133E8A88D2E313D90C32AD /* WindowFrameCalculatorTests.swift in Sources */, 0F133E8A88D2E313D90C32AD /* WindowFrameCalculatorTests.swift in Sources */,
154F363D434A26105C5999B5 /* WorkspaceRegistryTests.swift in Sources */, 154F363D434A26105C5999B5 /* WorkspaceRegistryTests.swift in Sources */,
@@ -454,11 +491,13 @@
D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */, D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */,
C84FA3100884649FE92BF5DD /* ContentView.swift in Sources */, C84FA3100884649FE92BF5DD /* ContentView.swift in Sources */,
34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */, 34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */,
FF6BB7E881BCBB4FD6CC4011 /* GhosttyBackendSession.swift in Sources */,
9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */, 9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */,
187F4B521BFC3BD29ADA79E3 /* HotkeyManager.swift in Sources */, 187F4B521BFC3BD29ADA79E3 /* HotkeyManager.swift in Sources */,
E792411FA82E79E810F4B4C3 /* HotkeyRecorderView.swift in Sources */, E792411FA82E79E810F4B4C3 /* HotkeyRecorderView.swift in Sources */,
6A18F6635B509FF58669F505 /* HotkeySettingsView.swift in Sources */, 6A18F6635B509FF58669F505 /* HotkeySettingsView.swift in Sources */,
12F68EDA880030DAB644FF5F /* LaunchAtLoginHelper.swift in Sources */, 12F68EDA880030DAB644FF5F /* LaunchAtLoginHelper.swift in Sources */,
4BA9D6274CFD6A87DC397B8E /* MouseAwareTerminalView.swift in Sources */,
A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */, A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */,
EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */, EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */,
3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */, 3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */,
@@ -472,6 +511,7 @@
771088361F981A9AAE976F3C /* SettingsBindings.swift in Sources */, 771088361F981A9AAE976F3C /* SettingsBindings.swift in Sources */,
DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */, DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */,
5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */, 5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */,
28F609FCF422E735F567EE32 /* SwiftTermBackendSession.swift in Sources */,
5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */, 5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */,
88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */, 88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */,
DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */, DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */,
@@ -511,14 +551,26 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements; CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = G698BP272N;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"Vendor\"",
);
INFOPLIST_FILE = CommandNotch/Resources/Info.plist; INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
OTHER_LDFLAGS = (
"$(inherited)",
"-lstdc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app; PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
PRODUCT_NAME = CommandNotch; PRODUCT_NAME = CommandNotch;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx; SDKROOT = macosx;
}; };
name = Debug; name = Debug;
@@ -610,14 +662,26 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements; CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = G698BP272N;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"Vendor\"",
);
INFOPLIST_FILE = CommandNotch/Resources/Info.plist; INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
OTHER_LDFLAGS = (
"$(inherited)",
"-lstdc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app; PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
PRODUCT_NAME = CommandNotch; PRODUCT_NAME = CommandNotch;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx; SDKROOT = macosx;
}; };
name = Release; name = Release;

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1600"
version = "1.7"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES" buildImplicitDependencies = "YES">
runPostActionsOnFailure = "NO">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
@@ -27,8 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES">
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@@ -51,8 +49,7 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
<TestableReference <TestableReference
skipped = "NO" skipped = "NO">
parallelizable = "NO">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4" BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4"
@@ -62,8 +59,6 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@@ -85,8 +80,6 @@
ReferencedContainer = "container:CommandNotch.xcodeproj"> ReferencedContainer = "container:CommandNotch.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"
@@ -104,8 +97,6 @@
ReferencedContainer = "container:CommandNotch.xcodeproj"> ReferencedContainer = "container:CommandNotch.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction> </ProfileAction>
<AnalyzeAction <AnalyzeAction
buildConfiguration = "Debug"> buildConfiguration = "Debug">

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1600"
version = "1.7"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES" buildImplicitDependencies = "YES">
runPostActionsOnFailure = "NO">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
@@ -27,8 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES">
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@@ -51,8 +49,7 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
<TestableReference <TestableReference
skipped = "NO" skipped = "NO">
parallelizable = "NO">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4" BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4"
@@ -62,13 +59,11 @@
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Release" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -85,8 +80,6 @@
ReferencedContainer = "container:CommandNotch.xcodeproj"> ReferencedContainer = "container:CommandNotch.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"
@@ -104,8 +97,6 @@
ReferencedContainer = "container:CommandNotch.xcodeproj"> ReferencedContainer = "container:CommandNotch.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction> </ProfileAction>
<AnalyzeAction <AnalyzeAction
buildConfiguration = "Debug"> buildConfiguration = "Debug">

View File

@@ -0,0 +1,49 @@
import AppKit
import SwiftTerm
struct TerminalScrollWheelRouter {
static func shouldSendMouseWheel(
allowMouseReporting: Bool,
mouseMode: Terminal.MouseMode,
deltaY: Double
) -> Bool {
allowMouseReporting && mouseMode != .off && deltaY != 0
}
static func velocity(for deltaY: Double) -> Int {
let magnitude = Int(abs(deltaY))
if magnitude > 9 {
return 20
}
if magnitude > 5 {
return 10
}
if magnitude > 1 {
return 3
}
return 1
}
static func gridPosition(
point: CGPoint,
bounds: CGRect,
cols: Int,
rows: Int
) -> (x: Int, y: Int, pixelX: Int, pixelY: Int) {
let safeCols = max(cols, 1)
let safeRows = max(rows, 1)
let width = max(bounds.width, 1)
let height = max(bounds.height, 1)
let clampedX = min(max(point.x, 0), width)
let clampedY = min(max(point.y, 0), height)
let cellWidth = width / CGFloat(safeCols)
let cellHeight = height / CGFloat(safeRows)
let column = min(max(Int(clampedX / cellWidth), 0), safeCols - 1)
let row = min(max(Int((height - clampedY) / cellHeight), 0), safeRows - 1)
let pixelX = min(max(Int(clampedX), 0), Int(width))
let pixelY = min(max(Int(height - clampedY), 0), Int(height))
return (column, row, pixelX, pixelY)
}
}

View File

@@ -1,10 +1,9 @@
import SwiftUI import SwiftUI
import SwiftTerm
/// NSViewRepresentable wrapper that embeds a SwiftTerm TerminalView. /// NSViewRepresentable wrapper that embeds the active terminal backend view.
/// The container has a solid black background matching the notch panel. /// The container has a solid black background matching the notch panel.
/// All transparency is handled by the single `.opacity()` on ContentView. /// All transparency is handled by the single `.opacity()` on ContentView.
struct SwiftTermView: NSViewRepresentable { struct TerminalSessionView: NSViewRepresentable {
let session: TerminalSession let session: TerminalSession
@@ -17,30 +16,30 @@ struct SwiftTermView: NSViewRepresentable {
} }
func updateNSView(_ nsView: NSView, context: Context) { func updateNSView(_ nsView: NSView, context: Context) {
let tv = session.terminalView let terminalView = session.view
if nsView.subviews.first !== tv { if nsView.subviews.first !== terminalView {
nsView.subviews.forEach { $0.removeFromSuperview() } nsView.subviews.forEach { $0.removeFromSuperview() }
embedTerminalView(in: nsView) embedTerminalView(in: nsView)
} }
DispatchQueue.main.async { DispatchQueue.main.async {
if let window = nsView.window, window.isKeyWindow { if let window = nsView.window, window.isKeyWindow {
window.makeFirstResponder(tv) session.focus()
} }
} }
} }
private func embedTerminalView(in container: NSView) { private func embedTerminalView(in container: NSView) {
let tv = session.terminalView let terminalView = session.view
tv.removeFromSuperview() terminalView.removeFromSuperview()
container.addSubview(tv) container.addSubview(terminalView)
tv.translatesAutoresizingMaskIntoConstraints = false terminalView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
tv.topAnchor.constraint(equalTo: container.topAnchor), terminalView.topAnchor.constraint(equalTo: container.topAnchor),
tv.bottomAnchor.constraint(equalTo: container.bottomAnchor), terminalView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
tv.leadingAnchor.constraint(equalTo: container.leadingAnchor), terminalView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
tv.trailingAnchor.constraint(equalTo: container.trailingAnchor), terminalView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
]) ])
} }
} }

View File

@@ -1,5 +1,4 @@
import SwiftUI import SwiftUI
import SwiftTerm
/// Main view rendered inside each NotchWindow. /// Main view rendered inside each NotchWindow.
/// ///
@@ -194,7 +193,7 @@ struct ContentView: View {
// Terminal fills remaining space // Terminal fills remaining space
if let session = workspace.activeTab { if let session = workspace.activeTab {
SwiftTermView(session: session) TerminalSessionView(session: session)
.id(session.id) .id(session.id)
.padding(.leading, 10) .padding(.leading, 10)
.padding(.trailing, 10) .padding(.trailing, 10)

View File

@@ -41,7 +41,7 @@ class PopoutWindowController: NSObject, NSWindowDelegate {
win.isReleasedWhenClosed = false win.isReleasedWhenClosed = false
let hostingView = NSHostingView( let hostingView = NSHostingView(
rootView: SwiftTermView(session: session) rootView: TerminalSessionView(session: session)
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.black) .background(Color.black)
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
@@ -67,10 +67,10 @@ class PopoutWindowController: NSObject, NSWindowDelegate {
func windowDidBecomeKey(_ notification: Notification) { func windowDidBecomeKey(_ notification: Notification) {
guard let window = notification.object as? NSWindow, guard let window = notification.object as? NSWindow,
let entry = windows.first(where: { $0.value === window }), let entry = windows.first(where: { $0.value === window }),
let terminalView = sessions[entry.key]?.terminalView, let terminalView = sessions[entry.key]?.view,
terminalView.window === window else { return } terminalView.window === window else { return }
window.makeFirstResponder(terminalView) sessions[entry.key]?.focus()
} }
func windowWillClose(_ notification: Notification) { func windowWillClose(_ notification: Notification) {

View File

@@ -186,7 +186,7 @@ final class ScreenManager: ObservableObject {
guard let self else { return } guard let self else { return }
self.windowCoordinator.focusActiveTerminal(for: screenID) { [weak self] in self.windowCoordinator.focusActiveTerminal(for: screenID) { [weak self] in
self?.screenRegistry.workspaceController(for: screenID).activeTab?.terminalView self?.screenRegistry.workspaceController(for: screenID).activeTab?.view
} }
} }
@@ -283,7 +283,7 @@ extension ScreenManager: NotchPresentationHost {
} }
windowCoordinator.presentOpen(for: screenID) { [weak self] in windowCoordinator.presentOpen(for: screenID) { [weak self] in
self?.screenRegistry.workspaceController(for: screenID).activeTab?.terminalView self?.screenRegistry.workspaceController(for: screenID).activeTab?.view
} }
} }

View File

@@ -49,6 +49,8 @@ struct AppSettings: Equatable, Codable {
shellPath: NotchSettings.Defaults.terminalShell, shellPath: NotchSettings.Defaults.terminalShell,
themeRawValue: NotchSettings.Defaults.terminalTheme, themeRawValue: NotchSettings.Defaults.terminalTheme,
scrollbackLines: NotchSettings.Defaults.terminalScrollbackLines, scrollbackLines: NotchSettings.Defaults.terminalScrollbackLines,
backendRawValue: NotchSettings.Defaults.terminalBackend,
termTypeRawValue: NotchSettings.Defaults.terminalTermType,
sizePresetsJSON: NotchSettings.Defaults.terminalSizePresets sizePresetsJSON: NotchSettings.Defaults.terminalSizePresets
), ),
hotkeys: HotkeySettings( hotkeys: HotkeySettings(
@@ -108,12 +110,22 @@ extension AppSettings {
var shellPath: String var shellPath: String
var themeRawValue: String var themeRawValue: String
var scrollbackLines: Int var scrollbackLines: Int
var backendRawValue: String
var termTypeRawValue: String
var sizePresetsJSON: String var sizePresetsJSON: String
var theme: TerminalTheme { var theme: TerminalTheme {
TerminalTheme.resolve(themeRawValue) TerminalTheme.resolve(themeRawValue)
} }
var backend: TerminalBackendPreference {
TerminalBackendPreference.resolve(backendRawValue)
}
var termType: TerminalTermTypePreference {
TerminalTermTypePreference.resolve(termTypeRawValue)
}
var sizePresets: [TerminalSizePreset] { var sizePresets: [TerminalSizePreset] {
TerminalSizePresetStore.decodePresets(from: sizePresetsJSON) ?? TerminalSizePresetStore.loadDefaults() TerminalSizePresetStore.decodePresets(from: sizePresetsJSON) ?? TerminalSizePresetStore.loadDefaults()
} }
@@ -158,6 +170,8 @@ struct TerminalSessionConfiguration: Equatable {
var theme: TerminalTheme var theme: TerminalTheme
var shellPath: String var shellPath: String
var scrollbackLines: Int var scrollbackLines: Int
var backendPreference: TerminalBackendPreference
var termTypePreference: TerminalTermTypePreference
} }
@MainActor @MainActor
@@ -166,3 +180,80 @@ protocol TerminalSessionConfigurationProviding: AnyObject {
var hotkeySettings: AppSettings.HotkeySettings { get } var hotkeySettings: AppSettings.HotkeySettings { get }
var terminalSizePresets: [TerminalSizePreset] { get } var terminalSizePresets: [TerminalSizePreset] { get }
} }
enum TerminalBackendPreference: String, CaseIterable, Codable, Identifiable {
case ghostty
case swiftTerm = "swiftterm"
var id: String { rawValue }
var label: String {
switch self {
case .ghostty:
return "Ghostty"
case .swiftTerm:
return "SwiftTerm"
}
}
var detail: String {
switch self {
case .ghostty:
return "Higher-fidelity terminal emulation with Ghostty's renderer and input pipeline."
case .swiftTerm:
return "Legacy backend with simpler integration and a smaller compatibility surface."
}
}
static func resolve(_ rawValue: String) -> TerminalBackendPreference {
TerminalBackendPreference(rawValue: rawValue) ?? .ghostty
}
}
enum TerminalTermTypePreference: String, CaseIterable, Codable, Identifiable {
case automatic
case xterm256color = "xterm-256color"
case xtermGhostty = "xterm-ghostty"
var id: String { rawValue }
var label: String {
switch self {
case .automatic:
return "Automatic"
case .xterm256color:
return "xterm-256color"
case .xtermGhostty:
return "xterm-ghostty"
}
}
var detail: String {
switch self {
case .automatic:
return "Uses a backend-specific default TERM value."
case .xterm256color:
return "Safest choice for SSH sessions and remote hosts with older terminfo setups."
case .xtermGhostty:
return "Advertises Ghostty's terminfo entry for maximum local capability."
}
}
func resolvedTermValue(for backend: TerminalBackendPreference) -> String {
switch self {
case .automatic:
switch backend {
case .ghostty:
return Self.xtermGhostty.rawValue
case .swiftTerm:
return Self.xterm256color.rawValue
}
case .xterm256color, .xtermGhostty:
return rawValue
}
}
static func resolve(_ rawValue: String) -> TerminalTermTypePreference {
TerminalTermTypePreference(rawValue: rawValue) ?? .automatic
}
}

View File

@@ -47,7 +47,9 @@ final class AppSettingsController: ObservableObject, TerminalSessionConfiguratio
fontSize: CGFloat(settings.terminal.fontSize), fontSize: CGFloat(settings.terminal.fontSize),
theme: settings.terminal.theme, theme: settings.terminal.theme,
shellPath: settings.terminal.shellPath, shellPath: settings.terminal.shellPath,
scrollbackLines: settings.terminal.scrollbackLines scrollbackLines: settings.terminal.scrollbackLines,
backendPreference: settings.terminal.backend,
termTypePreference: settings.terminal.termType
) )
} }

View File

@@ -53,6 +53,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
shellPath: string(NotchSettings.Keys.terminalShell, default: NotchSettings.Defaults.terminalShell), shellPath: string(NotchSettings.Keys.terminalShell, default: NotchSettings.Defaults.terminalShell),
themeRawValue: string(NotchSettings.Keys.terminalTheme, default: NotchSettings.Defaults.terminalTheme), themeRawValue: string(NotchSettings.Keys.terminalTheme, default: NotchSettings.Defaults.terminalTheme),
scrollbackLines: integer(NotchSettings.Keys.terminalScrollbackLines, default: NotchSettings.Defaults.terminalScrollbackLines), scrollbackLines: integer(NotchSettings.Keys.terminalScrollbackLines, default: NotchSettings.Defaults.terminalScrollbackLines),
backendRawValue: string(NotchSettings.Keys.terminalBackend, default: NotchSettings.Defaults.terminalBackend),
termTypeRawValue: string(NotchSettings.Keys.terminalTermType, default: NotchSettings.Defaults.terminalTermType),
sizePresetsJSON: string(NotchSettings.Keys.terminalSizePresets, default: NotchSettings.Defaults.terminalSizePresets) sizePresetsJSON: string(NotchSettings.Keys.terminalSizePresets, default: NotchSettings.Defaults.terminalSizePresets)
), ),
hotkeys: .init( hotkeys: .init(
@@ -103,6 +105,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
defaults.set(settings.terminal.shellPath, forKey: NotchSettings.Keys.terminalShell) defaults.set(settings.terminal.shellPath, forKey: NotchSettings.Keys.terminalShell)
defaults.set(settings.terminal.themeRawValue, forKey: NotchSettings.Keys.terminalTheme) defaults.set(settings.terminal.themeRawValue, forKey: NotchSettings.Keys.terminalTheme)
defaults.set(settings.terminal.scrollbackLines, forKey: NotchSettings.Keys.terminalScrollbackLines) defaults.set(settings.terminal.scrollbackLines, forKey: NotchSettings.Keys.terminalScrollbackLines)
defaults.set(settings.terminal.backendRawValue, forKey: NotchSettings.Keys.terminalBackend)
defaults.set(settings.terminal.termTypeRawValue, forKey: NotchSettings.Keys.terminalTermType)
defaults.set(settings.terminal.sizePresetsJSON, forKey: NotchSettings.Keys.terminalSizePresets) defaults.set(settings.terminal.sizePresetsJSON, forKey: NotchSettings.Keys.terminalSizePresets)
defaults.set(settings.hotkeys.toggle.toJSON(), forKey: NotchSettings.Keys.hotkeyToggle) defaults.set(settings.hotkeys.toggle.toJSON(), forKey: NotchSettings.Keys.hotkeyToggle)

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,8 @@ enum NotchSettings {
static let terminalShell = "terminalShell" static let terminalShell = "terminalShell"
static let terminalTheme = "terminalTheme" static let terminalTheme = "terminalTheme"
static let terminalScrollbackLines = "terminalScrollbackLines" static let terminalScrollbackLines = "terminalScrollbackLines"
static let terminalBackend = "terminalBackend"
static let terminalTermType = "terminalTermType"
static let terminalSizePresets = "terminalSizePresets" static let terminalSizePresets = "terminalSizePresets"
static let workspaceSummaries = "workspaceSummaries" static let workspaceSummaries = "workspaceSummaries"
static let screenAssignments = "screenAssignments" static let screenAssignments = "screenAssignments"
@@ -100,6 +102,8 @@ enum NotchSettings {
static let terminalShell: String = "" static let terminalShell: String = ""
static let terminalTheme: String = TerminalTheme.terminalApp.rawValue static let terminalTheme: String = TerminalTheme.terminalApp.rawValue
static let terminalScrollbackLines: Int = 500 static let terminalScrollbackLines: Int = 500
static let terminalBackend: String = TerminalBackendPreference.ghostty.rawValue
static let terminalTermType: String = TerminalTermTypePreference.automatic.rawValue
static let terminalSizePresets: String = TerminalSizePresetStore.defaultPresetsJSON() static let terminalSizePresets: String = TerminalSizePresetStore.defaultPresetsJSON()
// Default hotkey bindings as JSON // Default hotkey bindings as JSON
@@ -151,6 +155,8 @@ enum NotchSettings {
Keys.terminalShell: Defaults.terminalShell, Keys.terminalShell: Defaults.terminalShell,
Keys.terminalTheme: Defaults.terminalTheme, Keys.terminalTheme: Defaults.terminalTheme,
Keys.terminalScrollbackLines: Defaults.terminalScrollbackLines, Keys.terminalScrollbackLines: Defaults.terminalScrollbackLines,
Keys.terminalBackend: Defaults.terminalBackend,
Keys.terminalTermType: Defaults.terminalTermType,
Keys.terminalSizePresets: Defaults.terminalSizePresets, Keys.terminalSizePresets: Defaults.terminalSizePresets,
Keys.hotkeyToggle: Defaults.hotkeyToggle, Keys.hotkeyToggle: Defaults.hotkeyToggle,

View File

@@ -0,0 +1,337 @@
import AppKit
import Darwin
import SwiftTerm
/// SwiftTerm-backed terminal backend used by the app-owned `TerminalSession`.
@MainActor
final class SwiftTermBackendSession: NSObject, TerminalBackendSession, LocalProcessDelegate, @preconcurrency TerminalViewDelegate {
let terminalView: TerminalView
var onTitleChange: ((String) -> Void)?
var onCurrentDirectoryChange: ((String?) -> Void)?
var onExit: (() -> Void)?
private var process: LocalProcess?
private var keyEventMonitor: Any?
private var scrollEventMonitor: Any?
private var backgroundColor = NSColor.black
private let configuredShellPath: String
private let termTypePreference: TerminalTermTypePreference
private let launchDirectory: String
private let scrollCoordinator = TerminalScrollCoordinator()
init(
fontSize: CGFloat,
theme: TerminalTheme,
shellPath: String,
scrollbackLines: Int,
termTypePreference: TerminalTermTypePreference,
initialDirectory: String
) {
terminalView = TerminalView(frame: NSRect(x: 0, y: 0, width: 600, height: 300))
configuredShellPath = shellPath
self.termTypePreference = termTypePreference
launchDirectory = initialDirectory
super.init()
terminalView.terminalDelegate = self
installOsc52ClipboardHandler()
updateFontSize(fontSize)
updateTheme(theme)
updateScrollbackLines(scrollbackLines)
installCommandArrowMonitor()
installScrollWheelMonitor()
}
deinit {
if let keyEventMonitor {
NSEvent.removeMonitor(keyEventMonitor)
}
if let scrollEventMonitor {
NSEvent.removeMonitor(scrollEventMonitor)
}
}
var view: NSView {
terminalView
}
func start() {
guard process == nil else { return }
let shellPath = resolveShell()
let shellName = (shellPath as NSString).lastPathComponent
let loginExecName = "-\(shellName)"
let proc = LocalProcess(delegate: self)
// Launch as a login shell so user startup files initialize PATH/tools.
proc.startProcess(
executable: shellPath,
args: ["-l"],
environment: terminalEnvironment(),
execName: loginExecName,
currentDirectory: launchDirectory
)
process = proc
onTitleChange?(shellName)
onCurrentDirectoryChange?(launchDirectory)
}
func terminate() {
process?.terminate()
process = nil
}
func focus() {
guard let window = terminalView.window else { return }
window.makeFirstResponder(terminalView)
}
func updateFontSize(_ size: CGFloat) {
terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
}
func updateTheme(_ theme: TerminalTheme) {
backgroundColor = theme.backgroundColor
terminalView.nativeBackgroundColor = backgroundColor
terminalView.nativeForegroundColor = theme.foregroundColor
terminalView.installColors(theme.ansiColors)
}
func updateScrollbackLines(_ scrollbackLines: Int) {
let sanitizedScrollbackLines = max(0, scrollbackLines)
terminalView.getTerminal().changeHistorySize(sanitizedScrollbackLines)
}
private func resolveShell() -> String {
let custom = configuredShellPath.trimmingCharacters(in: .whitespacesAndNewlines)
if !custom.isEmpty && FileManager.default.isExecutableFile(atPath: custom) {
return custom
}
let environmentShell = ProcessInfo.processInfo.environment["SHELL"]?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if FileManager.default.isExecutableFile(atPath: environmentShell) {
return environmentShell
}
return "/bin/zsh"
}
private func terminalEnvironment() -> [String] {
var environment = ProcessInfo.processInfo.environment
environment["TERM"] = termTypePreference.resolvedTermValue(for: .swiftTerm)
environment["COLORTERM"] = "truecolor"
return environment
.sorted { $0.key < $1.key }
.map { "\($0.key)=\($0.value)" }
}
private func installOsc52ClipboardHandler() {
let maxPayloadSize = 1_048_576 // 1 MB
terminalView.getTerminal().registerOscHandler(code: 52) { data in
guard data.count >= 2,
data[data.startIndex] == UInt8(ascii: "c"),
data[data.startIndex + 1] == UInt8(ascii: ";") else { return }
let base64 = Data(data[(data.startIndex + 2)...])
guard let content = Data(base64Encoded: base64),
content.count <= maxPayloadSize,
let string = String(data: content, encoding: .utf8) else { return }
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(string, forType: .string)
}
}
private func installCommandArrowMonitor() {
keyEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
guard let self else { return event }
guard let window = self.terminalView.window else { return event }
guard event.window === window else { return event }
guard window.firstResponder === self.terminalView else { return event }
guard let sequence = TerminalCommandArrowBehavior.sequence(
for: event.modifierFlags,
keyCode: event.keyCode,
applicationCursor: self.terminalView.getTerminal().applicationCursor
) else {
return event
}
self.terminalView.send(data: sequence[...])
return nil
}
}
private func installScrollWheelMonitor() {
scrollEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .scrollWheel) { [weak self] event in
guard let self else { return event }
guard let window = self.terminalView.window else { return event }
guard event.window === window else { return event }
guard window.firstResponder === self.terminalView else { return event }
let terminal = self.terminalView.getTerminal()
guard TerminalScrollWheelRouter.shouldSendMouseWheel(
allowMouseReporting: self.terminalView.allowMouseReporting,
mouseMode: terminal.mouseMode,
deltaY: event.deltaY
) else {
return event
}
let localPoint = self.terminalView.convert(event.locationInWindow, from: nil)
let dims = terminal.getDims()
let hit = TerminalScrollWheelRouter.gridPosition(
point: localPoint,
bounds: self.terminalView.bounds,
cols: dims.cols,
rows: dims.rows
)
let button = event.deltaY > 0 ? 4 : 5
let flags = terminal.encodeButton(
button: button,
release: false,
shift: event.modifierFlags.contains(.shift),
meta: event.modifierFlags.contains(.option),
control: event.modifierFlags.contains(.control)
)
for _ in 0..<TerminalScrollWheelRouter.velocity(for: event.deltaY) {
terminal.sendEvent(
buttonFlags: flags,
x: hit.x,
y: hit.y,
pixelX: hit.pixelX,
pixelY: hit.pixelY
)
}
return nil
}
}
// MARK: - LocalProcessDelegate
nonisolated func processTerminated(_ source: LocalProcess, exitCode: Int32?) {
_ = source
_ = exitCode
Task { @MainActor in
self.process = nil
self.resetTerminalModes()
self.onExit?()
}
}
private func resetTerminalModes() {
let resetSequences: [[UInt8]] = [
Array("\u{1b}[?9l".utf8),
Array("\u{1b}[?1000l".utf8),
Array("\u{1b}[?1002l".utf8),
Array("\u{1b}[?1003l".utf8),
Array("\u{1b}[?1006l".utf8),
Array("\u{1b}[?1015l".utf8),
Array("\u{1b}[?2004l".utf8),
Array("\u{1b}[?1l".utf8),
Array("\u{1b}[?1049l".utf8),
Array("\u{1b}[?25h".utf8),
]
for seq in resetSequences {
terminalView.feed(byteArray: seq[...])
}
}
nonisolated func dataReceived(slice: ArraySlice<UInt8>) {
let data = slice
Task { @MainActor in
if let restorePosition = self.scrollCoordinator.outputRestorePosition(canScroll: self.terminalView.canScroll) {
self.scrollCoordinator.suppressTracking {
self.terminalView.feed(byteArray: data)
self.terminalView.scroll(toPosition: restorePosition)
}
} else {
self.terminalView.feed(byteArray: data)
}
}
}
nonisolated func getWindowSize() -> winsize {
var ws = winsize()
ws.ws_col = 80
ws.ws_row = 24
return ws
}
// MARK: - TerminalViewDelegate
func send(source: TerminalView, data: ArraySlice<UInt8>) {
_ = source
if scrollCoordinator.userDidStartTyping() {
terminalView.scroll(toPosition: 1)
}
process?.send(data: data)
}
func setTerminalTitle(source: TerminalView, title: String) {
_ = source
onTitleChange?(title)
}
func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) {
_ = source
guard newCols > 0, newRows > 0 else { return }
guard let proc = process else { return }
let fd = proc.childfd
guard fd >= 0 else { return }
var ws = winsize()
ws.ws_col = UInt16(newCols)
ws.ws_row = UInt16(newRows)
_ = ioctl(fd, TIOCSWINSZ, &ws)
}
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
_ = source
onCurrentDirectoryChange?(directory)
}
func scrolled(source: TerminalView, position: Double) {
scrollCoordinator.terminalDidScroll(to: position, canScroll: source.canScroll)
}
func rangeChanged(source: TerminalView, startY: Int, endY: Int) {
_ = source
_ = startY
_ = endY
}
func clipboardCopy(source: TerminalView, content: Data) {
_ = source
NSPasteboard.general.clearContents()
NSPasteboard.general.setData(content, forType: .string)
}
func requestOpenLink(source: TerminalView, link: String, params: [String : String]) {
_ = source
_ = params
if let url = URL(string: link) {
NSWorkspace.shared.open(url)
}
}
func bell(source: TerminalView) {
_ = source
NSSound.beep()
}
func iTermContent(source: TerminalView, content: ArraySlice<UInt8>) {
_ = source
_ = content
}
}

View File

@@ -1,7 +1,179 @@
import AppKit import AppKit
import SwiftTerm
import Combine import Combine
@MainActor
protocol TerminalBackendSession: AnyObject {
var view: NSView { get }
var onTitleChange: ((String) -> Void)? { get set }
var onCurrentDirectoryChange: ((String?) -> Void)? { get set }
var onExit: (() -> Void)? { get set }
func start()
func terminate()
func focus()
func updateFontSize(_ size: CGFloat)
func updateTheme(_ theme: TerminalTheme)
func updateScrollbackLines(_ scrollbackLines: Int)
}
@MainActor
final class TerminalSession: ObservableObject, Identifiable {
let id = UUID()
@Published var title: String = "shell"
@Published var isRunning: Bool = true
@Published var currentDirectory: String?
private let backend: any TerminalBackendSession
init(
backend: any TerminalBackendSession,
initialDirectory: String? = nil,
startImmediately: Bool = true
) {
self.backend = backend
currentDirectory = Self.resolveInitialDirectory(initialDirectory)
backend.onTitleChange = { [weak self] title in
self?.title = title.isEmpty ? "shell" : title
}
backend.onCurrentDirectoryChange = { [weak self] directory in
guard let self,
let normalizedDirectory = Self.normalizedDirectory(directory) else { return }
self.currentDirectory = normalizedDirectory
}
backend.onExit = { [weak self] in
self?.isRunning = false
}
if startImmediately {
start()
} else {
isRunning = false
}
}
convenience init(
fontSize: CGFloat,
theme: TerminalTheme,
shellPath: String,
scrollbackLines: Int,
backendPreference: TerminalBackendPreference,
termTypePreference: TerminalTermTypePreference,
initialDirectory: String? = nil,
startImmediately: Bool = true
) {
let resolvedDirectory = Self.resolveInitialDirectory(initialDirectory)
let backend = Self.makeBackend(
fontSize: fontSize,
theme: theme,
shellPath: shellPath,
scrollbackLines: scrollbackLines,
backendPreference: backendPreference,
termTypePreference: termTypePreference,
initialDirectory: resolvedDirectory
)
self.init(
backend: backend,
initialDirectory: resolvedDirectory,
startImmediately: startImmediately
)
}
var view: NSView {
backend.view
}
func start() {
isRunning = true
backend.start()
}
func focus() {
backend.focus()
}
func terminate() {
backend.terminate()
isRunning = false
}
func updateFontSize(_ size: CGFloat) {
backend.updateFontSize(size)
}
func applyTheme(_ theme: TerminalTheme) {
backend.updateTheme(theme)
}
func updateScrollbackLines(_ scrollbackLines: Int) {
backend.updateScrollbackLines(scrollbackLines)
}
static func resolveInitialDirectory(_ directory: String?) -> String {
normalizedDirectory(directory) ?? NSHomeDirectory()
}
static func normalizedDirectory(_ directory: String?) -> String? {
let trimmed = directory?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !trimmed.isEmpty else {
return nil
}
if let url = URL(string: trimmed), url.isFileURL {
return url.path(percentEncoded: false)
}
return (trimmed as NSString).expandingTildeInPath
}
private static func makeBackend(
fontSize: CGFloat,
theme: TerminalTheme,
shellPath: String,
scrollbackLines: Int,
backendPreference: TerminalBackendPreference,
termTypePreference: TerminalTermTypePreference,
initialDirectory: String
) -> any TerminalBackendSession {
let resolvedBackendPreference = resolvedBackendPreference(configured: backendPreference)
if resolvedBackendPreference == .ghostty,
let backend = GhosttyBackendSession.makeIfAvailable(
fontSize: fontSize,
theme: theme,
shellPath: shellPath,
scrollbackLines: scrollbackLines,
termTypePreference: termTypePreference,
initialDirectory: initialDirectory
) {
return backend
}
return SwiftTermBackendSession(
fontSize: fontSize,
theme: theme,
shellPath: shellPath,
scrollbackLines: scrollbackLines,
termTypePreference: termTypePreference,
initialDirectory: initialDirectory
)
}
private static func resolvedBackendPreference(configured: TerminalBackendPreference) -> TerminalBackendPreference {
let environmentOverride = ProcessInfo.processInfo.environment["COMMANDNOTCH_TERMINAL_BACKEND"]?.lowercased() ?? ""
switch environmentOverride {
case TerminalBackendPreference.swiftTerm.rawValue:
return .swiftTerm
case TerminalBackendPreference.ghostty.rawValue:
return .ghostty
default:
return configured
}
}
}
/// Tracks whether the terminal viewport should follow live output or preserve /// Tracks whether the terminal viewport should follow live output or preserve
/// the user's current scrollback position. /// the user's current scrollback position.
final class TerminalScrollCoordinator { final class TerminalScrollCoordinator {
@@ -54,260 +226,44 @@ final class TerminalScrollCoordinator {
} }
} }
/// Wraps a single SwiftTerm TerminalView + LocalProcess pair. /// Suppresses stale mouse-report forwarding when an interactive command exits
@MainActor /// without restoring terminal mouse modes cleanly.
class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @preconcurrency TerminalViewDelegate { final class TerminalMouseCaptureCoordinator {
private(set) var suppressesMouseReporting = false
let id = UUID() @discardableResult
let terminalView: TerminalView func commandDidFinish(mouseCaptured: Bool) -> Bool {
private var process: LocalProcess? suppressesMouseReporting = mouseCaptured
private var keyEventMonitor: Any? return mouseCaptured
private let backgroundColor = NSColor.black
private let configuredShellPath: String
private var scrollbackLines: Int
private let launchDirectory: String
private let scrollCoordinator = TerminalScrollCoordinator()
@Published var title: String = "shell"
@Published var isRunning: Bool = true
@Published var currentDirectory: String?
init(
fontSize: CGFloat,
theme: TerminalTheme,
shellPath: String,
scrollbackLines: Int,
initialDirectory: String? = nil,
startImmediately: Bool = true
) {
terminalView = TerminalView(frame: NSRect(x: 0, y: 0, width: 600, height: 300))
configuredShellPath = shellPath
self.scrollbackLines = max(0, scrollbackLines)
launchDirectory = Self.resolveInitialDirectory(initialDirectory)
currentDirectory = launchDirectory
super.init()
terminalView.terminalDelegate = self
installOsc52ClipboardHandler()
let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular)
terminalView.font = font
applyTheme(theme)
updateScrollbackLines(self.scrollbackLines)
installCommandArrowMonitor()
if startImmediately {
startShell()
} else {
isRunning = false
}
} }
deinit { func shouldBypassEnhancedKeyboardInput(mouseCaptured: Bool) -> Bool {
if let keyEventMonitor { if !suppressesMouseReporting {
NSEvent.removeMonitor(keyEventMonitor) return false
}
} }
// MARK: - Shell management if !mouseCaptured {
suppressesMouseReporting = false
private func startShell() { return false
let shellPath = resolveShell()
let shellName = (shellPath as NSString).lastPathComponent
let loginExecName = "-\(shellName)"
let proc = LocalProcess(delegate: self)
// Launch as a login shell so user startup files initialize PATH/tools.
proc.startProcess(
executable: shellPath,
args: ["-l"],
environment: nil,
execName: loginExecName,
currentDirectory: launchDirectory
)
process = proc
title = shellName
} }
private static func resolveInitialDirectory(_ directory: String?) -> String { return true
normalizedDirectory(directory) ?? NSHomeDirectory()
} }
private static func normalizedDirectory(_ directory: String?) -> String? { func shouldForwardMouseInput(mouseCaptured: Bool) -> Bool {
let trimmed = directory?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" if !suppressesMouseReporting {
guard !trimmed.isEmpty else { return true
return nil
} }
if let url = URL(string: trimmed), url.isFileURL { if !mouseCaptured {
return url.path(percentEncoded: false) suppressesMouseReporting = false
return true
} }
return (trimmed as NSString).expandingTildeInPath return false
} }
private func resolveShell() -> String { func userDidSubmitCommand() {
let custom = configuredShellPath.trimmingCharacters(in: .whitespacesAndNewlines) suppressesMouseReporting = false
if !custom.isEmpty && FileManager.default.isExecutableFile(atPath: custom) {
return custom
} }
return ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
}
private func installOsc52ClipboardHandler() {
let maxPayloadSize = 1_048_576 // 1 MB
terminalView.getTerminal().registerOscHandler(code: 52) { [weak self] data in
guard data.count >= 2,
data[data.startIndex] == UInt8(ascii: "c"),
data[data.startIndex + 1] == UInt8(ascii: ";") else { return }
let base64 = Data(data[(data.startIndex + 2)...])
guard let content = Data(base64Encoded: base64),
content.count <= maxPayloadSize,
let string = String(data: content, encoding: .utf8) else { return }
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(string, forType: .string)
}
}
private func installCommandArrowMonitor() {
keyEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
guard let self else { return event }
guard let window = self.terminalView.window else { return event }
guard event.window === window else { return event }
guard window.firstResponder === self.terminalView else { return event }
guard let sequence = TerminalCommandArrowBehavior.sequence(
for: event.modifierFlags,
keyCode: event.keyCode,
applicationCursor: self.terminalView.getTerminal().applicationCursor
) else {
return event
}
self.terminalView.send(data: sequence[...])
return nil
}
}
func updateFontSize(_ size: CGFloat) {
terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
}
func applyTheme(_ theme: TerminalTheme) {
// Keep the notch visually consistent while swapping the terminal's
// default foreground color and ANSI palette for command output.
terminalView.nativeBackgroundColor = backgroundColor
terminalView.nativeForegroundColor = theme.foregroundColor
terminalView.installColors(theme.ansiColors)
}
func updateScrollbackLines(_ scrollbackLines: Int) {
let sanitizedScrollbackLines = max(0, scrollbackLines)
self.scrollbackLines = sanitizedScrollbackLines
terminalView.getTerminal().changeHistorySize(sanitizedScrollbackLines)
}
func terminate() {
process?.terminate()
process = nil
isRunning = false
}
// MARK: - LocalProcessDelegate
nonisolated func processTerminated(_ source: LocalProcess, exitCode: Int32?) {
Task { @MainActor in
self.isRunning = false
self.resetTerminalModes()
}
}
private func resetTerminalModes() {
let resetSequences: [[UInt8]] = [
Array("\u{1b}[?9l".utf8),
Array("\u{1b}[?1000l".utf8),
Array("\u{1b}[?1002l".utf8),
Array("\u{1b}[?1003l".utf8),
Array("\u{1b}[?1006l".utf8),
Array("\u{1b}[?1015l".utf8),
Array("\u{1b}[?2004l".utf8),
Array("\u{1b}[?1l".utf8),
Array("\u{1b}[?1049l".utf8),
Array("\u{1b}[?25h".utf8),
]
for seq in resetSequences {
terminalView.feed(byteArray: seq[...])
}
}
nonisolated func dataReceived(slice: ArraySlice<UInt8>) {
let data = slice
Task { @MainActor in
if let restorePosition = self.scrollCoordinator.outputRestorePosition(canScroll: self.terminalView.canScroll) {
self.scrollCoordinator.suppressTracking {
self.terminalView.feed(byteArray: data)
self.terminalView.scroll(toPosition: restorePosition)
}
} else {
self.terminalView.feed(byteArray: data)
}
}
}
nonisolated func getWindowSize() -> winsize {
var ws = winsize()
ws.ws_col = 80
ws.ws_row = 24
return ws
}
// MARK: - TerminalViewDelegate
func send(source: TerminalView, data: ArraySlice<UInt8>) {
if scrollCoordinator.userDidStartTyping() {
terminalView.scroll(toPosition: 1)
}
process?.send(data: data)
}
func setTerminalTitle(source: TerminalView, title: String) {
self.title = title.isEmpty ? "shell" : title
}
func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) {
guard newCols > 0, newRows > 0 else { return }
guard let proc = process else { return }
let fd = proc.childfd
guard fd >= 0 else { return }
var ws = winsize()
ws.ws_col = UInt16(newCols)
ws.ws_row = UInt16(newRows)
_ = ioctl(fd, TIOCSWINSZ, &ws)
}
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
guard let normalizedDirectory = Self.normalizedDirectory(directory) else { return }
currentDirectory = normalizedDirectory
}
func scrolled(source: TerminalView, position: Double) {
scrollCoordinator.terminalDidScroll(to: position, canScroll: source.canScroll)
}
func rangeChanged(source: TerminalView, startY: Int, endY: Int) {}
func clipboardCopy(source: TerminalView, content: Data) {
NSPasteboard.general.clearContents()
NSPasteboard.general.setData(content, forType: .string)
}
func requestOpenLink(source: TerminalView, link: String, params: [String : String]) {
if let url = URL(string: link) { NSWorkspace.shared.open(url) }
}
func bell(source: TerminalView) { NSSound.beep() }
func iTermContent(source: TerminalView, content: ArraySlice<UInt8>) {}
} }

View File

@@ -50,6 +50,21 @@ enum TerminalTheme: String, CaseIterable, Identifiable {
} }
} }
var backgroundColor: NSColor {
switch self {
case .terminalApp:
return Self.nsColor(0x000000)
case .xterm:
return Self.nsColor(0x000000)
case .solarizedDark:
return Self.nsColor(0x002B36)
case .dracula:
return Self.nsColor(0x282A36)
case .nord:
return Self.nsColor(0x2E3440)
}
}
var ansiColors: [Color] { var ansiColors: [Color] {
switch self { switch self {
case .terminalApp: case .terminalApp:

View File

@@ -8,6 +8,8 @@ protocol TerminalSessionFactoryType {
theme: TerminalTheme, theme: TerminalTheme,
shellPath: String, shellPath: String,
scrollbackLines: Int, scrollbackLines: Int,
backendPreference: TerminalBackendPreference,
termTypePreference: TerminalTermTypePreference,
initialDirectory: String? initialDirectory: String?
) -> TerminalSession ) -> TerminalSession
} }
@@ -18,6 +20,8 @@ struct LiveTerminalSessionFactory: TerminalSessionFactoryType {
theme: TerminalTheme, theme: TerminalTheme,
shellPath: String, shellPath: String,
scrollbackLines: Int, scrollbackLines: Int,
backendPreference: TerminalBackendPreference,
termTypePreference: TerminalTermTypePreference,
initialDirectory: String? initialDirectory: String?
) -> TerminalSession { ) -> TerminalSession {
TerminalSession( TerminalSession(
@@ -25,6 +29,8 @@ struct LiveTerminalSessionFactory: TerminalSessionFactoryType {
theme: theme, theme: theme,
shellPath: shellPath, shellPath: shellPath,
scrollbackLines: scrollbackLines, scrollbackLines: scrollbackLines,
backendPreference: backendPreference,
termTypePreference: termTypePreference,
initialDirectory: initialDirectory initialDirectory: initialDirectory
) )
} }
@@ -110,6 +116,8 @@ final class WorkspaceController: ObservableObject {
theme: config.theme, theme: config.theme,
shellPath: config.shellPath, shellPath: config.shellPath,
scrollbackLines: config.scrollbackLines, scrollbackLines: config.scrollbackLines,
backendPreference: config.backendPreference,
termTypePreference: config.termTypePreference,
initialDirectory: activeTab?.currentDirectory initialDirectory: activeTab?.currentDirectory
) )

View File

@@ -0,0 +1,382 @@
# bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions.
# https://github.com/rcaloras/bash-preexec
#
#
# 'preexec' functions are executed before each interactive command is
# executed, with the interactive command as its argument. The 'precmd'
# function is executed before each prompt is displayed.
#
# Author: Ryan Caloras (ryan@bashhub.com)
# Forked from Original Author: Glyph Lefkowitz
#
# V0.6.0
#
# General Usage:
#
# 1. Source this file at the end of your bash profile so as not to interfere
# with anything else that's using PROMPT_COMMAND.
#
# 2. Add any precmd or preexec functions by appending them to their arrays:
# e.g.
# precmd_functions+=(my_precmd_function)
# precmd_functions+=(some_other_precmd_function)
#
# preexec_functions+=(my_preexec_function)
#
# 3. Consider changing anything using the DEBUG trap or PROMPT_COMMAND
# to use preexec and precmd instead. Preexisting usages will be
# preserved, but doing so manually may be less surprising.
#
# Note: This module requires two Bash features which you must not otherwise be
# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override
# either of these after bash-preexec has been installed it will most likely break.
# Tell shellcheck what kind of file this is.
# shellcheck shell=bash
# Make sure this is bash that's running and return otherwise.
# Use POSIX syntax for this line:
if [ -z "${BASH_VERSION-}" ]; then
return 1
fi
# We only support Bash 3.1+.
# Note: BASH_VERSINFO is first available in Bash-2.0.
if [[ -z "${BASH_VERSINFO-}" ]] || (( BASH_VERSINFO[0] < 3 || (BASH_VERSINFO[0] == 3 && BASH_VERSINFO[1] < 1) )); then
return 1
fi
# Avoid duplicate inclusion
if [[ -n "${bash_preexec_imported:-}" || -n "${__bp_imported:-}" ]]; then
return 0
fi
bash_preexec_imported="defined"
# WARNING: This variable is no longer used and should not be relied upon.
# Use ${bash_preexec_imported} instead.
# shellcheck disable=SC2034
__bp_imported="${bash_preexec_imported}"
# Should be available to each precmd and preexec
# functions, should they want it. $? and $_ are available as $? and $_, but
# $PIPESTATUS is available only in a copy, $BP_PIPESTATUS.
# TODO: Figure out how to restore PIPESTATUS before each precmd or preexec
# function.
__bp_last_ret_value="$?"
BP_PIPESTATUS=("${PIPESTATUS[@]}")
__bp_last_argument_prev_command="$_"
__bp_inside_precmd=0
__bp_inside_preexec=0
# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
__bp_install_string=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'
# Fails if any of the given variables are readonly
# Reference https://stackoverflow.com/a/4441178
__bp_require_not_readonly() {
local var
for var; do
if ! ( unset "$var" 2> /dev/null ); then
echo "bash-preexec requires write access to ${var}" >&2
return 1
fi
done
}
# Remove ignorespace and or replace ignoreboth from HISTCONTROL
# so we can accurately invoke preexec with a command from our
# history even if it starts with a space.
__bp_adjust_histcontrol() {
local histcontrol
histcontrol="${HISTCONTROL:-}"
histcontrol="${histcontrol//ignorespace}"
# Replace ignoreboth with ignoredups
if [[ "$histcontrol" == *"ignoreboth"* ]]; then
histcontrol="ignoredups:${histcontrol//ignoreboth}"
fi
export HISTCONTROL="$histcontrol"
}
# This variable describes whether we are currently in "interactive mode";
# i.e. whether this shell has just executed a prompt and is waiting for user
# input. It documents whether the current command invoked by the trace hook is
# run interactively by the user; it's set immediately after the prompt hook,
# and unset as soon as the trace hook is run.
__bp_preexec_interactive_mode=""
# These arrays are used to add functions to be run before, or after, prompts.
declare -a precmd_functions
declare -a preexec_functions
# Trims leading and trailing whitespace from $2 and writes it to the variable
# name passed as $1
__bp_trim_whitespace() {
local var=${1:?} text=${2:-}
text="${text#"${text%%[![:space:]]*}"}" # remove leading whitespace characters
text="${text%"${text##*[![:space:]]}"}" # remove trailing whitespace characters
printf -v "$var" '%s' "$text"
}
# Trims whitespace and removes any leading or trailing semicolons from $2 and
# writes the resulting string to the variable name passed as $1. Used for
# manipulating substrings in PROMPT_COMMAND
__bp_sanitize_string() {
local var=${1:?} text=${2:-} sanitized
__bp_trim_whitespace sanitized "$text"
sanitized=${sanitized%;}
sanitized=${sanitized#;}
__bp_trim_whitespace sanitized "$sanitized"
printf -v "$var" '%s' "$sanitized"
}
# This function is installed as part of the PROMPT_COMMAND;
# It sets a variable to indicate that the prompt was just displayed,
# to allow the DEBUG trap to know that the next command is likely interactive.
__bp_interactive_mode() {
__bp_preexec_interactive_mode="on"
}
# This function is installed as part of the PROMPT_COMMAND.
# It will invoke any functions defined in the precmd_functions array.
__bp_precmd_invoke_cmd() {
# Save the returned value from our last command, and from each process in
# its pipeline. Note: this MUST be the first thing done in this function.
# BP_PIPESTATUS may be unused, ignore
# shellcheck disable=SC2034
__bp_last_ret_value="$?" BP_PIPESTATUS=("${PIPESTATUS[@]}")
# Don't invoke precmds if we are inside an execution of an "original
# prompt command" by another precmd execution loop. This avoids infinite
# recursion.
if (( __bp_inside_precmd > 0 )); then
return
fi
local __bp_inside_precmd=1
# Invoke every function defined in our function array.
local precmd_function
for precmd_function in "${precmd_functions[@]}"; do
# Only execute this function if it actually exists.
# Test existence of functions with: declare -[Ff]
if type -t "$precmd_function" 1>/dev/null; then
__bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command"
# Quote our function invocation to prevent issues with IFS
"$precmd_function"
fi
done
__bp_set_ret_value "$__bp_last_ret_value"
}
# Sets a return value in $?. We may want to get access to the $? variable in our
# precmd functions. This is available for instance in zsh. We can simulate it in bash
# by setting the value here.
__bp_set_ret_value() {
return ${1:+"$1"}
}
__bp_in_prompt_command() {
local prompt_command_array IFS=$'\n;'
read -rd '' -a prompt_command_array <<< "${PROMPT_COMMAND[*]:-}"
local trimmed_arg
__bp_trim_whitespace trimmed_arg "${1:-}"
local command trimmed_command
for command in "${prompt_command_array[@]:-}"; do
__bp_trim_whitespace trimmed_command "$command"
if [[ "$trimmed_command" == "$trimmed_arg" ]]; then
return 0
fi
done
return 1
}
# This function is installed as the DEBUG trap. It is invoked before each
# interactive prompt display. Its purpose is to inspect the current
# environment to attempt to detect if the current command is being invoked
# interactively, and invoke 'preexec' if so.
__bp_preexec_invoke_exec() {
# Save the contents of $_ so that it can be restored later on.
# https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702
__bp_last_argument_prev_command="${1:-}"
# Don't invoke preexecs if we are inside of another preexec.
if (( __bp_inside_preexec > 0 )); then
return
fi
local __bp_inside_preexec=1
# Checks if the file descriptor is not standard out (i.e. '1')
# __bp_delay_install checks if we're in test. Needed for bats to run.
# Prevents preexec from being invoked for functions in PS1
if [[ ! -t 1 && -z "${__bp_delay_install:-}" ]]; then
return
fi
if [[ -n "${COMP_POINT:-}" || -n "${READLINE_POINT:-}" ]]; then
# We're in the middle of a completer or a keybinding set up by "bind
# -x". This obviously can't be an interactively issued command.
return
fi
if [[ -z "${__bp_preexec_interactive_mode:-}" ]]; then
# We're doing something related to displaying the prompt. Let the
# prompt set the title instead of me.
return
else
# If we're in a subshell, then the prompt won't be re-displayed to put
# us back into interactive mode, so let's not set the variable back.
# In other words, if you have a subshell like
# (sleep 1; sleep 2)
# You want to see the 'sleep 2' as a set_command_title as well.
if [[ 0 -eq "${BASH_SUBSHELL:-}" ]]; then
__bp_preexec_interactive_mode=""
fi
fi
if __bp_in_prompt_command "${BASH_COMMAND:-}"; then
# If we're executing something inside our prompt_command then we don't
# want to call preexec. Bash prior to 3.1 can't detect this at all :/
__bp_preexec_interactive_mode=""
return
fi
local this_command
this_command=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1)
this_command="${this_command#*[[:digit:]][* ] }"
# Sanity check to make sure we have something to invoke our function with.
if [[ -z "$this_command" ]]; then
return
fi
# Invoke every function defined in our function array.
local preexec_function
local preexec_function_ret_value
local preexec_ret_value=0
for preexec_function in "${preexec_functions[@]:-}"; do
# Only execute each function if it actually exists.
# Test existence of function with: declare -[fF]
if type -t "$preexec_function" 1>/dev/null; then
__bp_set_ret_value "${__bp_last_ret_value:-}"
# Quote our function invocation to prevent issues with IFS
"$preexec_function" "$this_command"
preexec_function_ret_value="$?"
if [[ "$preexec_function_ret_value" != 0 ]]; then
preexec_ret_value="$preexec_function_ret_value"
fi
fi
done
# Restore the last argument of the last executed command, and set the return
# value of the DEBUG trap to be the return code of the last preexec function
# to return an error.
# If `extdebug` is enabled a non-zero return value from any preexec function
# will cause the user's command not to execute.
# Run `shopt -s extdebug` to enable
__bp_set_ret_value "$preexec_ret_value" "$__bp_last_argument_prev_command"
}
__bp_install() {
# Exit if we already have this installed.
if [[ "${PROMPT_COMMAND[*]:-}" == *"__bp_precmd_invoke_cmd"* ]]; then
return 1
fi
trap '__bp_preexec_invoke_exec "$_"' DEBUG
# Preserve any prior DEBUG trap as a preexec function
eval "local trap_argv=(${__bp_trap_string:-})"
local prior_trap=${trap_argv[2]:-}
unset __bp_trap_string
if [[ -n "$prior_trap" ]]; then
eval '__bp_original_debug_trap() {
'"$prior_trap"'
}'
preexec_functions+=(__bp_original_debug_trap)
fi
# Adjust our HISTCONTROL Variable if needed.
#
# GHOSTTY: Don't modify HISTCONTROL. This hack is only needed to improve the
# accuracy of the command argument passed to the preexec functions, and we
# don't use that argument in our bash shell integration script (and nor does
# the __bp_original_debug_trap function above, which is the only other active
# preexec function).
#__bp_adjust_histcontrol
# Issue #25. Setting debug trap for subshells causes sessions to exit for
# backgrounded subshell commands (e.g. (pwd)& ). Believe this is a bug in Bash.
#
# Disabling this by default. It can be enabled by setting this variable.
if [[ -n "${__bp_enable_subshells:-}" ]]; then
# Set so debug trap will work be invoked in subshells.
set -o functrace > /dev/null 2>&1
shopt -s extdebug > /dev/null 2>&1
fi
local existing_prompt_command
# Remove setting our trap install string and sanitize the existing prompt command string
existing_prompt_command="${PROMPT_COMMAND:-}"
# Edge case of appending to PROMPT_COMMAND
existing_prompt_command="${existing_prompt_command//$__bp_install_string/:}" # no-op
existing_prompt_command="${existing_prompt_command//$'\n':$'\n'/$'\n'}" # remove known-token only
existing_prompt_command="${existing_prompt_command//$'\n':;/$'\n'}" # remove known-token only
__bp_sanitize_string existing_prompt_command "$existing_prompt_command"
if [[ "${existing_prompt_command:-:}" == ":" ]]; then
existing_prompt_command=
fi
# Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
# actually entered something.
PROMPT_COMMAND='__bp_precmd_invoke_cmd'
PROMPT_COMMAND+=${existing_prompt_command:+$'\n'$existing_prompt_command}
if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) )); then
PROMPT_COMMAND+=('__bp_interactive_mode')
else
# shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
PROMPT_COMMAND+=$'\n__bp_interactive_mode'
fi
# Add two functions to our arrays for convenience
# of definition.
precmd_functions+=(precmd)
preexec_functions+=(preexec)
# Invoke our two functions manually that were added to $PROMPT_COMMAND
__bp_precmd_invoke_cmd
__bp_interactive_mode
}
# Sets an installation string as part of our PROMPT_COMMAND to install
# after our session has started. This allows bash-preexec to be included
# at any point in our bash profile.
__bp_install_after_session_init() {
# bash-preexec needs to modify these variables in order to work correctly
# if it can't, just stop the installation
__bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return
local sanitized_prompt_command
__bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
if [[ -n "$sanitized_prompt_command" ]]; then
# shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
fi
# shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
PROMPT_COMMAND+=${__bp_install_string}
}
# Run our install so long as we're not delaying it.
if [[ -z "${__bp_delay_install:-}" ]]; then
__bp_install_after_session_init
fi

View File

@@ -0,0 +1,324 @@
# Parts of this script are based on Kitty's bash integration. Kitty is
# distributed under GPLv3, so this file is also distributed under GPLv3.
# The license header is reproduced below:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# We need to be in interactive mode to proceed.
if [[ "$-" != *i* ]]; then builtin return; fi
# When automatic shell integration is active, we were started in POSIX
# mode and need to manually recreate the bash startup sequence.
if [ -n "$GHOSTTY_BASH_INJECT" ]; then
# Store a temporary copy of our startup flags and unset these global
# environment variables so we can safely handle reentrancy.
builtin declare __ghostty_bash_flags="$GHOSTTY_BASH_INJECT"
builtin unset ENV GHOSTTY_BASH_INJECT
# Restore an existing ENV that was replaced by the shell integration code.
if [[ -n "$GHOSTTY_BASH_ENV" ]]; then
builtin export ENV=$GHOSTTY_BASH_ENV
builtin unset GHOSTTY_BASH_ENV
fi
# Restore bash's default 'posix' behavior. Also reset 'inherit_errexit',
# which doesn't happen as part of the 'posix' reset.
builtin set +o posix
builtin shopt -u inherit_errexit 2>/dev/null
# Unexport HISTFILE if it was set by the shell integration code.
if [[ -n "$GHOSTTY_BASH_UNEXPORT_HISTFILE" ]]; then
builtin export -n HISTFILE
builtin unset GHOSTTY_BASH_UNEXPORT_HISTFILE
fi
# Manually source the startup files. See INVOCATION in bash(1) and
# run_startup_files() in shell.c in the Bash source code.
if builtin shopt -q login_shell; then
if [[ $__ghostty_bash_flags != *"--noprofile"* ]]; then
[ -r /etc/profile ] && builtin source "/etc/profile"
for __ghostty_rcfile in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do
[ -r "$__ghostty_rcfile" ] && {
builtin source "$__ghostty_rcfile"
break
}
done
fi
else
if [[ $__ghostty_bash_flags != *"--norc"* ]]; then
# The location of the system bashrc is determined at bash build
# time via -DSYS_BASHRC and can therefore vary across distros:
# Arch, Debian, Ubuntu use /etc/bash.bashrc
# Fedora uses /etc/bashrc sourced from ~/.bashrc instead of SYS_BASHRC
# Void Linux uses /etc/bash/bashrc
# Nixos uses /etc/bashrc
for __ghostty_rcfile in /etc/bash.bashrc /etc/bash/bashrc /etc/bashrc; do
[ -r "$__ghostty_rcfile" ] && {
builtin source "$__ghostty_rcfile"
break
}
done
if [[ -z "$GHOSTTY_BASH_RCFILE" ]]; then GHOSTTY_BASH_RCFILE="$HOME/.bashrc"; fi
[ -r "$GHOSTTY_BASH_RCFILE" ] && builtin source "$GHOSTTY_BASH_RCFILE"
fi
fi
builtin unset __ghostty_rcfile
builtin unset __ghostty_bash_flags
builtin unset GHOSTTY_BASH_RCFILE
fi
# Add Ghostty binary to PATH if the path feature is enabled
if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* && -n "$GHOSTTY_BIN_DIR" ]]; then
if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then
export PATH="$PATH:$GHOSTTY_BIN_DIR"
fi
fi
# Sudo
if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* && -n "$TERMINFO" ]]; then
# Wrap `sudo` command to ensure Ghostty terminfo is preserved.
#
# This approach supports wrapping a `sudo` alias, but the alias definition
# must come _after_ this function is defined. Otherwise, the alias expansion
# will take precedence over this function, and it won't be wrapped.
function sudo {
builtin local sudo_has_sudoedit_flags="no"
for arg in "$@"; do
# Check if argument is '-e' or '--edit' (sudoedit flags)
if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
sudo_has_sudoedit_flags="yes"
builtin break
fi
# Check if argument is neither an option nor a key-value pair
if [[ "$arg" != -* && "$arg" != *=* ]]; then
builtin break
fi
done
if [[ "$sudo_has_sudoedit_flags" == "yes" ]]; then
builtin command sudo "$@"
else
builtin command sudo --preserve-env=TERMINFO "$@"
fi
}
fi
# SSH Integration
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
function ssh() {
builtin local ssh_term ssh_opts
ssh_term="xterm-256color"
ssh_opts=()
# Configure environment variables for remote session
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-env* ]]; then
ssh_opts+=(-o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
fi
# Install terminfo on remote host if needed
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-terminfo* ]]; then
builtin local ssh_user ssh_hostname
while IFS=' ' read -r ssh_key ssh_value; do
case "$ssh_key" in
user) ssh_user="$ssh_value" ;;
hostname) ssh_hostname="$ssh_value" ;;
esac
[[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
done < <(builtin command ssh -G "$@" 2>/dev/null)
if [[ -n "$ssh_hostname" ]]; then
builtin local ssh_target="${ssh_user}@${ssh_hostname}"
# Check if terminfo is already cached
if "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1; then
ssh_term="xterm-ghostty"
elif builtin command -v infocmp >/dev/null 2>&1; then
builtin local ssh_terminfo ssh_cpath_dir ssh_cpath
ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
if [[ -n "$ssh_terminfo" ]]; then
builtin echo "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
ssh_cpath="$ssh_cpath_dir/socket"
if builtin echo "$ssh_terminfo" | builtin command ssh -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null; then
ssh_term="xterm-ghostty"
ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation
"$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
else
builtin echo "Warning: Failed to install terminfo." >&2
fi
else
builtin echo "Warning: Could not generate terminfo data." >&2
fi
else
builtin echo "Warning: ghostty command not available for cache management." >&2
fi
fi
fi
# Execute SSH with TERM environment variable
TERM="$ssh_term" COLORTERM=truecolor builtin command ssh "${ssh_opts[@]}" "$@"
}
fi
# This is set to 1 when we're executing a command so that we don't
# send prompt marks multiple times.
_ghostty_executing=""
_ghostty_last_reported_cwd=""
function __ghostty_precmd() {
local ret="$?"
if test "$_ghostty_executing" != "0"; then
_GHOSTTY_SAVE_PS1="$PS1"
_GHOSTTY_SAVE_PS2="$PS2"
# Use 133;P (not 133;A) inside PS1 to avoid fresh-line behavior on
# readline redraws (e.g., vi mode switches, Ctrl-L). The initial
# 133;A with fresh-line is emitted once via printf below.
PS1='\[\e]133;P;k=i\a\]'$PS1'\[\e]133;B\a\]'
PS2='\[\e]133;P;k=s\a\]'$PS2'\[\e]133;B\a\]'
# Bash doesn't redraw the leading lines in a multiline prompt so we mark
# the start of each line (after each newline) as a secondary prompt. This
# correctly handles multiline prompts by setting the first to primary and
# the subsequent lines to secondary.
#
# We only replace the \n prompt escape, not literal newlines ($'\n'),
# because literal newlines may appear inside $(...) command substitutions
# where inserting escape sequences would break shell syntax.
if [[ "$PS1" == *"\n"* ]]; then
PS1="${PS1//\\n/\\n$'\\[\\e]133;P;k=s\\a\\]'}"
fi
# Cursor
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
builtin local cursor=5 # blinking bar
[[ "$GHOSTTY_SHELL_FEATURES" == *"cursor:steady"* ]] && cursor=6 # steady bar
[[ "$PS1" != *"\[\e[${cursor} q\]"* ]] && PS1=$PS1"\[\e[${cursor} q\]"
[[ "$PS0" != *'\[\e[0 q\]'* ]] && PS0=$PS0'\[\e[0 q\]' # reset
fi
# Title (working directory)
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
PS1=$PS1'\[\e]2;\w\a\]'
fi
fi
if test "$_ghostty_executing" != ""; then
# End of current command. Report its status.
builtin printf "\e]133;D;%s;aid=%s\a" "$ret" "$BASHPID"
fi
# Fresh line and start of prompt. When ble.sh is active, emit 133;P instead
# of 133;A because ble.sh maintains its own cursor position tracking. 133;A's
# cursor movement (CR+LF when not at column 0) is invisible to ble.sh and
# desyncs its position state, causing display artifacts like duplicate
# prompts. See: https://github.com/akinomyoga/ble.sh/issues/684
if [[ -n "${BLE_VERSION-}" ]]; then
builtin printf "\e]133;P;k=i\a"
else
builtin printf "\e]133;A;redraw=last;cl=line;aid=%s\a" "$BASHPID"
fi
# unfortunately bash provides no hooks to detect cwd changes
# in particular this means cwd reporting will not happen for a
# command like cd /test && cat. PS0 is evaluated before cd is run.
if [[ "$_ghostty_last_reported_cwd" != "$PWD" ]]; then
_ghostty_last_reported_cwd="$PWD"
builtin printf "\e]7;kitty-shell-cwd://%s%s\a" "$HOSTNAME" "$PWD"
fi
_ghostty_executing=0
}
function __ghostty_preexec() {
builtin local cmd="$1"
PS1="$_GHOSTTY_SAVE_PS1"
PS2="$_GHOSTTY_SAVE_PS2"
# Title (current command)
if [[ -n $cmd && "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
builtin printf "\e]2;%s\a" "${cmd//[[:cntrl:]]/}"
fi
# End of input, start of output.
builtin printf "\e]133;C;\a"
_ghostty_executing=1
}
if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )); then
__ghostty_preexec_hook() {
builtin local cmd
cmd=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1)
cmd="${cmd#*[[:digit:]][* ] }" # remove leading history number
[[ -n "$cmd" ]] && __ghostty_preexec "$cmd"
}
__ghostty_hook() {
builtin local ret=$?
__ghostty_precmd "$ret"
# Append preexec hook to PS0 if not already present.
# Use function substitution in 5.3+, otherwise command substitution.
if [[ "$PS0" != *"__ghostty_preexec_hook"* ]]; then
if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3) )); then
# shellcheck disable=SC2016
PS0+='${ __ghostty_preexec_hook; }'
else
# shellcheck disable=SC2016
PS0+='$(__ghostty_preexec_hook >/dev/tty)'
fi
fi
}
# Append our hook to PROMPT_COMMAND, preserving its existing type.
#
# The 2>/dev/null suppresses "command not found" in subshells that inherit
# PROMPT_COMMAND without the function definition. This also silences any
# errors from inside __ghostty_hook itself, but those are all terminal escape
# sequences and non-actionable.
#
# shellcheck disable=SC2128,SC2178,SC2179
if [[ ";${PROMPT_COMMAND[*]:-};" != *";__ghostty_hook 2>/dev/null;"* ]]; then
if [[ -z "${PROMPT_COMMAND[*]}" ]]; then
if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) )); then
PROMPT_COMMAND=("__ghostty_hook 2>/dev/null")
else
PROMPT_COMMAND="__ghostty_hook 2>/dev/null"
fi
elif [[ $(builtin declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a "* ]]; then
PROMPT_COMMAND+=("__ghostty_hook 2>/dev/null")
else
[[ "${PROMPT_COMMAND}" =~ (\;[[:space:]]*|$'\n')$ ]] || PROMPT_COMMAND+=";"
PROMPT_COMMAND+="__ghostty_hook 2>/dev/null"
fi
fi
else
builtin source "$(dirname -- "${BASH_SOURCE[0]}")/bash-preexec.sh"
preexec_functions+=(__ghostty_preexec)
precmd_functions+=(__ghostty_precmd)
fi

View File

@@ -0,0 +1,190 @@
{
use platform
use str
# Clean up XDG_DATA_DIRS by removing GHOSTTY_SHELL_INTEGRATION_XDG_DIR
if (and (has-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR) (has-env XDG_DATA_DIRS)) {
set-env XDG_DATA_DIRS (str:replace $E:GHOSTTY_SHELL_INTEGRATION_XDG_DIR":" "" $E:XDG_DATA_DIRS)
unset-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR
}
# List of enabled shell integration features
var features = [(str:split ',' $E:GHOSTTY_SHELL_FEATURES)]
# State tracking for semantic prompt sequences
# Values: 'prompt-start', 'pre-exec', 'post-exec'
fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
fn mark-prompt-start {
if (not-eq $E:__ghostty_prompt_state 'prompt-start') {
printf "\e]133;D;aid="$pid"\a"
}
set-prompt-state 'prompt-start'
printf "\e]133;A;aid="$pid"\a"
}
fn mark-output-start {|_|
set-prompt-state 'pre-exec'
printf "\e]133;C\a"
}
fn mark-output-end {|cmd-info|
set-prompt-state 'post-exec'
var exit-status = 0
# in case of error: retrieve exit status,
# unless does not exist (= builtin function failure), then default to 1
if (not-eq $nil $cmd-info[error]) {
set exit-status = 1
if (has-key $cmd-info[error] reason) {
if (has-key $cmd-info[error][reason] exit-status) {
set exit-status = $cmd-info[error][reason][exit-status]
}
}
}
printf "\e]133;D;"$exit-status";aid="$pid"\a"
}
# NOTE: OSC 133;B (end of prompt, start of input) cannot be reliably
# implemented at the script level in Elvish. The prompt function's output is
# escaped, and writing to /dev/tty has timing issues because Elvish renders
# its prompts on a background thread. Full semantic prompt support requires a
# native implementation: https://github.com/elves/elvish/pull/1917
fn sudo-with-terminfo {|@args|
var sudoedit = $false
for arg $args {
if (str:has-prefix $arg --) {
if (eq $arg --edit) {
set sudoedit = $true
break
}
} elif (str:has-prefix $arg -) {
if (str:contains (str:trim-prefix $arg -) e) {
set sudoedit = $true
break
}
} elif (not (str:contains $arg =)) {
break
}
}
if (not $sudoedit) { set args = [ --preserve-env=TERMINFO $@args ] }
(external sudo) $@args
}
fn ssh-integration {|@args|
var ssh-term = "xterm-256color"
var ssh-opts = []
# Configure environment variables for remote session
if (has-value $features ssh-env) {
set ssh-opts = (conj $ssh-opts ^
-o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
}
if (has-value $features ssh-terminfo) {
var ssh-user = ""
var ssh-hostname = ""
# Parse ssh config
for line [((external ssh) -G $@args)] {
var parts = [(str:fields $line)]
if (> (count $parts) 1) {
var ssh-key = $parts[0]
var ssh-value = $parts[1]
if (eq $ssh-key user) {
set ssh-user = $ssh-value
} elif (eq $ssh-key hostname) {
set ssh-hostname = $ssh-value
}
if (and (not-eq $ssh-user "") (not-eq $ssh-hostname "")) {
break
}
}
}
if (not-eq $ssh-hostname "") {
var ghostty = $E:GHOSTTY_BIN_DIR/"ghostty"
var ssh-target = $ssh-user"@"$ssh-hostname
# Check if terminfo is already cached
if (bool ?($ghostty +ssh-cache --host=$ssh-target)) {
set ssh-term = "xterm-ghostty"
} elif (has-external infocmp) {
var ssh-terminfo = ((external infocmp) -0 -x xterm-ghostty 2>/dev/null | slurp)
if (not-eq $ssh-terminfo "") {
echo "Setting up xterm-ghostty terminfo on "$ssh-hostname"..." >&2
use os
var ssh-cpath-dir = (os:temp-dir "ghostty-ssh-"$ssh-user".*")
var ssh-cpath = $ssh-cpath-dir"/socket"
if (bool ?(echo $ssh-terminfo | (external ssh) $@ssh-opts -o ControlMaster=yes -o ControlPath=$ssh-cpath -o ControlPersist=60s $@args '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null)) {
set ssh-term = "xterm-ghostty"
set ssh-opts = (conj $ssh-opts -o ControlPath=$ssh-cpath)
# Cache successful installation
$ghostty +ssh-cache --add=$ssh-target >/dev/null
} else {
echo "Warning: Failed to install terminfo." >&2
}
} else {
echo "Warning: Could not generate terminfo data." >&2
}
} else {
echo "Warning: ghostty command not available for cache management." >&2
}
}
}
with [E:TERM = $ssh-term E:COLORTERM = truecolor] {
(external ssh) $@ssh-opts $@args
}
}
defer {
mark-prompt-start
}
set edit:before-readline = (conj $edit:before-readline $mark-prompt-start~)
set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
set edit:after-command = (conj $edit:after-command $mark-output-end~)
if (str:contains $E:GHOSTTY_SHELL_FEATURES "cursor") {
var cursor = "5" # blinking bar
if (has-value $features cursor:steady) {
set cursor = "6" # steady bar
}
fn beam { printf "\e["$cursor" q" }
fn reset { printf "\e[0 q" }
set edit:before-readline = (conj $edit:before-readline $beam~)
set edit:after-readline = (conj $edit:after-readline {|_| reset })
}
if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) {
if (not (has-value $paths $E:GHOSTTY_BIN_DIR)) {
set paths = [$@paths $E:GHOSTTY_BIN_DIR]
}
}
if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) {
edit:add-var sudo~ $sudo-with-terminfo~
}
if (and (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-) (has-external ssh)) {
edit:add-var ssh~ $ssh-integration~
}
# Report changes to the current directory.
fn report-pwd { printf "\e]7;kitty-shell-cwd://%s%s\a" (platform:hostname) $pwd }
set after-chdir = (conj $after-chdir {|_| report-pwd })
report-pwd
}

View File

@@ -0,0 +1,245 @@
# This shell script aims to be written in a way where it can't really fail
# or all failure scenarios are handled, so that we never leave the shell in
# a weird state. If you find a way to break this, please report a bug!
function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR value"
# If we don't have our own data dir then we don't need to do anything.
if not set -q GHOSTTY_SHELL_INTEGRATION_XDG_DIR
return
end
# If the data dir isn't set at all then we don't need to do anything.
if not set -q XDG_DATA_DIRS
return
end
# We need to do this so that XDG_DATA_DIRS turns into an array.
set --function --path xdg_data_dirs "$XDG_DATA_DIRS"
# If our data dir is in the list then remove it.
if set --function index (contains --index "$GHOSTTY_SHELL_INTEGRATION_XDG_DIR" $xdg_data_dirs)
set --erase --function xdg_data_dirs[$index]
end
# Re-export our data dir
if set -q xdg_data_dirs[1]
set --global --export --unpath XDG_DATA_DIRS "$xdg_data_dirs"
else
set --erase --global XDG_DATA_DIRS
end
set --erase GHOSTTY_SHELL_INTEGRATION_XDG_DIR
end
function ghostty_exit -d "exit the shell integration setup"
functions -e ghostty_restore_xdg_data_dir
functions -e ghostty_exit
exit 0
end
# We always try to restore the XDG data dir
ghostty_restore_xdg_data_dir
# If we aren't interactive or we've already run, don't run.
status --is-interactive || ghostty_exit
# We do the full setup on the first prompt render. We do this so that other
# shell integrations that setup the prompt and modify things are able to run
# first. We want to run _last_.
function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
functions -e __ghostty_setup
set --local features (string split , $GHOSTTY_SHELL_FEATURES)
# Parse the fish version for feature detection.
# Default to 0.0 if version is unavailable or malformed.
set -l fish_major 0
set -l fish_minor 0
if set -q version[1]
set -l fish_ver (string match -r '(\d+)\.(\d+)' -- $version[1])
if set -q fish_ver[2]; and test -n "$fish_ver[2]"
set fish_major "$fish_ver[2]"
end
if set -q fish_ver[3]; and test -n "$fish_ver[3]"
set fish_minor "$fish_ver[3]"
end
end
# Our OSC133A (prompt start) sequence. If we're using Fish >= 4.1
# then it supports click_events so we enable that.
set -g __ghostty_prompt_start_mark "\e]133;A\a"
if test "$fish_major" -gt 4; or test "$fish_major" -eq 4 -a "$fish_minor" -ge 1
set -g __ghostty_prompt_start_mark "\e]133;A;click_events=1\a"
end
if string match -q 'cursor*' -- $features
set -l cursor 5 # blinking bar
contains cursor:steady $features && set cursor 6 # steady bar
# Change the cursor to a beam on prompt.
function __ghostty_set_cursor_beam --on-event fish_prompt -V cursor -d "Set cursor shape"
if not functions -q fish_vi_cursor_handle
echo -en "\e[$cursor q"
end
end
function __ghostty_reset_cursor --on-event fish_preexec -d "Reset cursor shape"
if not functions -q fish_vi_cursor_handle
echo -en "\e[0 q"
end
end
end
# Add Ghostty binary to PATH if the path feature is enabled
if contains path $features; and test -n "$GHOSTTY_BIN_DIR"
fish_add_path --global --path --append "$GHOSTTY_BIN_DIR"
end
# When using sudo shell integration feature, ensure $TERMINFO is set
# and `sudo` is not already a function or alias
if contains sudo $features; and test -n "$TERMINFO"; and test file = (type -t sudo 2> /dev/null; or echo "x")
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
function sudo -d "Wrap sudo to preserve terminfo"
set --function sudo_has_sudoedit_flags no
for arg in $argv
# Check if argument is '-e' or '--edit' (sudoedit flags)
if string match -q -- -e "$arg"; or string match -q -- --edit "$arg"
set --function sudo_has_sudoedit_flags yes
break
end
# Check if argument is neither an option nor a key-value pair
if not string match -r -q -- "^-" "$arg"; and not string match -r -q -- "=" "$arg"
break
end
end
if test "$sudo_has_sudoedit_flags" = yes
command sudo $argv
else
command sudo --preserve-env=TERMINFO $argv
end
end
end
# SSH Integration
set -l features (string split ',' -- "$GHOSTTY_SHELL_FEATURES")
if contains ssh-env $features; or contains ssh-terminfo $features
function ssh --wraps=ssh --description "SSH wrapper with Ghostty integration"
set -l features (string split ',' -- "$GHOSTTY_SHELL_FEATURES")
set -l ssh_term xterm-256color
set -l ssh_opts
# Configure environment variables for remote session
if contains ssh-env $features
set -a ssh_opts -o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION"
end
# Install terminfo on remote host if needed
if contains ssh-terminfo $features
set -l ssh_user
set -l ssh_hostname
for line in (command ssh -G $argv 2>/dev/null)
set -l parts (string split ' ' -- $line)
if test (count $parts) -ge 2
switch $parts[1]
case user
set ssh_user $parts[2]
case hostname
set ssh_hostname $parts[2]
end
if test -n "$ssh_user"; and test -n "$ssh_hostname"
break
end
end
end
if test -n "$ssh_hostname"
set -l ssh_target "$ssh_user@$ssh_hostname"
# Check if terminfo is already cached
if test -x "$GHOSTTY_BIN_DIR/ghostty"; and "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1
set ssh_term xterm-ghostty
else if command -q infocmp
set -l ssh_terminfo
set -l ssh_cpath_dir
set -l ssh_cpath
set ssh_terminfo "$(infocmp -0 -x xterm-ghostty 2>/dev/null)"
if test -n "$ssh_terminfo"
echo "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
set ssh_cpath_dir (mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null; or echo "/tmp/ghostty-ssh-$ssh_user."(random))
set ssh_cpath "$ssh_cpath_dir/socket"
if echo "$ssh_terminfo" | command ssh $ssh_opts -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s $argv '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null
set ssh_term xterm-ghostty
set -a ssh_opts -o "ControlPath=$ssh_cpath"
# Cache successful installation
if test -x "$GHOSTTY_BIN_DIR/ghostty"
"$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1; or true
end
else
echo "Warning: Failed to install terminfo." >&2
end
else
echo "Warning: Could not generate terminfo data." >&2
end
else
echo "Warning: ghostty command not available for cache management." >&2
end
end
end
# Execute SSH with TERM environment variable
TERM="$ssh_term" COLORTERM=truecolor command ssh $ssh_opts $argv
end
end
# Setup prompt marking
function __ghostty_mark_prompt_start --on-event fish_prompt --on-event fish_posterror
# If we never got the output end event, then we need to send it now.
if test "$__ghostty_prompt_state" != prompt-start
echo -en "\e]133;D\a"
end
set --global __ghostty_prompt_state prompt-start
echo -en $__ghostty_prompt_start_mark
end
function __ghostty_mark_output_start --on-event fish_preexec
set --global __ghostty_prompt_state pre-exec
echo -en "\e]133;C\a"
end
function __ghostty_mark_output_end --on-event fish_postexec
set --global __ghostty_prompt_state post-exec
echo -en "\e]133;D;$status\a"
end
# Report pwd. This is actually built-in to fish but only for terminals
# that match an allowlist and that isn't us.
function __update_cwd_osc --on-variable PWD -d 'Notify capable terminals when $PWD changes'
if status --is-command-substitution || set -q INSIDE_EMACS
return
end
printf \e\]7\;file://%s%s\a $hostname (string escape --style=url $PWD)
end
# Enable fish to handle reflow because Ghostty clears the prompt on resize.
set --global fish_handle_reflow 1
# Initial calls for first prompt
if string match -q 'cursor*' -- $features
__ghostty_set_cursor_beam
end
__ghostty_mark_prompt_start
__update_cwd_osc
end
ghostty_exit

View File

@@ -0,0 +1,110 @@
# Ghostty shell integration
export module ghostty {
def has_feature [feature: string] {
$feature in ($env.GHOSTTY_SHELL_FEATURES | default "" | split row ',')
}
# Wrap `ssh` with Ghostty TERMINFO support
export def --wrapped ssh [...args] {
mut ssh_env = {}
mut ssh_opts = []
# `ssh-env`: use xterm-256color and propagate COLORTERM/TERM_PROGRAM vars
if (has_feature "ssh-env") {
$ssh_env.TERM = "xterm-256color"
$ssh_env.COLORTERM = "truecolor"
$ssh_opts = [
"-o" "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION"
]
}
# `ssh-terminfo`: auto-install xterm-ghostty terminfo on remote hosts
if (has_feature "ssh-terminfo") {
let ghostty = ($env.GHOSTTY_BIN_DIR? | default "") | path join "ghostty"
let ssh_cfg = ^ssh -G ...$args
| lines
| parse "{key} {value}"
| where key in ["user" "hostname"]
| select key value
| transpose -rd
| default {user: $env.USER hostname: "localhost"}
let ssh_id = $"($ssh_cfg.user)@($ssh_cfg.hostname)"
if (^$ghostty "+ssh-cache" $"--host=($ssh_id)" | complete | $in.exit_code == 0) {
$ssh_env.TERM = "xterm-ghostty"
} else {
$ssh_env.TERM = "xterm-256color"
let terminfo = try {
^infocmp -0 -x xterm-ghostty
} catch {
print -e "infocmp failed, using xterm-256color"
}
if ($terminfo | is-not-empty) {
print $"Setting up xterm-ghostty terminfo on ($ssh_cfg.hostname)..."
let ctrl_path = (
mktemp -td $"ghostty-ssh-($ssh_cfg.user).XXXXXX"
| path join "socket"
)
let remote_args = $ssh_opts ++ [
"-o" "ControlMaster=yes"
"-o" $"ControlPath=($ctrl_path)"
"-o" "ControlPersist=60s"
] ++ $args
$terminfo | ^ssh ...$remote_args '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1'
| complete
| if $in.exit_code == 0 {
^$ghostty "+ssh-cache" $"--add=($ssh_id)" e>| print -e
$ssh_env.TERM = "xterm-ghostty"
$ssh_opts = ($ssh_opts ++ ["-o" $"ControlPath=($ctrl_path)"])
} else {
print -e "terminfo install failed, using xterm-256color"
}
}
}
}
let ssh_args = $ssh_opts ++ $args
with-env $ssh_env {
^ssh ...$ssh_args
}
}
# Wrap `sudo` to preserve Ghostty's TERMINFO environment variable
export def --wrapped sudo [...args] {
mut sudo_args = $args
if (has_feature "sudo") {
# Extract just the sudo options (before the command)
let sudo_options = (
$args | take until {|arg|
not (($arg | str starts-with "-") or ($arg | str contains "="))
}
)
# Prepend TERMINFO preservation flag if not using sudoedit
if (not ("-e" in $sudo_options or "--edit" in $sudo_options)) {
$sudo_args = ($args | prepend "--preserve-env=TERMINFO")
}
}
^sudo ...$sudo_args
}
}
# Clean up XDG_DATA_DIRS by removing GHOSTTY_SHELL_INTEGRATION_XDG_DIR
if 'GHOSTTY_SHELL_INTEGRATION_XDG_DIR' in $env {
if 'XDG_DATA_DIRS' in $env {
$env.XDG_DATA_DIRS = ($env.XDG_DATA_DIRS | str replace $"($env.GHOSTTY_SHELL_INTEGRATION_XDG_DIR):" "")
}
hide-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR
}

View File

@@ -0,0 +1,61 @@
# Based on (started as) a copy of Kitty's zsh integration. Kitty is
# distributed under GPLv3, so this file is also distributed under GPLv3.
# The license header is reproduced below:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script is sourced automatically by zsh when ZDOTDIR is set to this
# directory. It therefore assumes it's running within our shell integration
# environment and should not be sourced manually (unlike ghostty-integration).
#
# This file can get sourced with aliases enabled. To avoid alias expansion
# we quote everything that can be quoted. Some aliases will still break us
# though.
# Restore the original ZDOTDIR value if GHOSTTY_ZSH_ZDOTDIR is set.
# Otherwise, unset the ZDOTDIR that was set during shell injection.
if [[ -n "${GHOSTTY_ZSH_ZDOTDIR+X}" ]]; then
'builtin' 'export' ZDOTDIR="$GHOSTTY_ZSH_ZDOTDIR"
'builtin' 'unset' 'GHOSTTY_ZSH_ZDOTDIR'
else
'builtin' 'unset' 'ZDOTDIR'
fi
# Use try-always to have the right error code.
{
# Zsh treats unset ZDOTDIR as if it was HOME. We do the same.
#
# Source the user's .zshenv before sourcing ghostty-integration because the
# former might set fpath and other things without which ghostty-integration
# won't work.
#
# Use typeset in case we are in a function with warn_create_global in
# effect. Unlikely but better safe than sorry.
'builtin' 'typeset' _ghostty_file=${ZDOTDIR-$HOME}"/.zshenv"
# Zsh ignores unreadable rc files. We do the same.
# Zsh ignores rc files that are directories, and so does source.
[[ ! -r "$_ghostty_file" ]] || 'builtin' 'source' '--' "$_ghostty_file"
} always {
if [[ -o 'interactive' ]]; then
# ${(%):-%x} is the path to the current file.
# On top of it we add :A:h to get the directory.
'builtin' 'typeset' _ghostty_file="${${(%):-%x}:A:h}"/ghostty-integration
if [[ -r "$_ghostty_file" ]]; then
'builtin' 'autoload' '-Uz' '--' "$_ghostty_file"
"${_ghostty_file:t}"
'builtin' 'unfunction' '--' "${_ghostty_file:t}"
fi
fi
'builtin' 'unset' '_ghostty_file'
}

View File

@@ -0,0 +1,454 @@
# vim:ft=zsh
#
# Based on (started as) a copy of Kitty's zsh integration. Kitty is
# distributed under GPLv3, so this file is also distributed under GPLv3.
# The license header is reproduced below:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Enables integration between zsh and ghostty.
#
# This is an autoloadable function. It's invoked automatically in shells
# directly spawned by Ghostty but not in any other shells. For example, running
# `exec zsh`, `sudo -E zsh`, `tmux`, or plain `zsh` will create a shell where
# ghostty-integration won't automatically run. Zsh users who want integration with
# Ghostty in all shells should add the following lines to their .zshrc:
#
# if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
# source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
# fi
#
# Implementation note: We can assume that alias expansion is disabled in this
# file, so no need to quote defensively. We still have to defensively prefix all
# builtins with `builtin` to avoid accidentally invoking user-defined functions.
# We avoid `function` reserved word as an additional defensive measure.
# Note that updating options with `builtin emulate -L zsh` affects the global options
# if it's called outside of a function. So nearly all code has to be in functions.
_entrypoint() {
builtin emulate -L zsh -o no_warn_create_global -o no_aliases
[[ -o interactive ]] || builtin return 0 # non-interactive shell
(( ! $+_ghostty_state )) || builtin return 0 # already initialized
# We require zsh 5.1+ (released Sept 2015) for features like functions_source,
# introspection arrays, and array pattern substitution.
if ! { builtin autoload -- is-at-least 2>/dev/null && is-at-least 5.1; }; then
builtin echo "Zsh ${ZSH_VERSION} is too old for ghostty shell integration (5.1+ required)" >&2
builtin return 1
fi
# 0: no OSC 133 [AC] marks have been written yet.
# 1: the last written OSC 133 C has not been closed with D yet.
# 2: none of the above.
builtin typeset -gi _ghostty_state
# Attempt to create a writable file descriptor to the TTY so that we can print
# to the TTY later even when STDOUT is redirected. This code is fairly subtle.
#
# - It's tempting to do `[[ -t 1 ]] && exec {_ghostty_state}>&1` but we cannot do this
# because it'll create a file descriptor >= 10 without O_CLOEXEC. This file
# descriptor will leak to child processes.
# - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes
# but it'll still leak if the current process is replaced with another. In
# addition, it'll break user code that relies on fd 3 being available.
# - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with
# O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via
# sysopen.
# - `zmodload zsh/system` and `sysopen -o cloexec -wu _ghostty_fd -- /dev/tty` can
# fail with an error message to STDERR (the latter can happen even if /dev/tty
# is writable), hence the redirection of STDERR. We do it for the whole block
# for performance reasons (redirections are slow).
# - We must open the file descriptor right here rather than in _ghostty_deferred_init
# because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`
# and then close the file descriptor more than once while suppressing errors.
# This could end up closing our file descriptor if we opened it in
# _ghostty_deferred_init.
typeset -gi _ghostty_fd
{
builtin zmodload zsh/system && (( $+builtins[sysopen] )) && {
{ [[ -w $TTY ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- $TTY } ||
{ [[ -w /dev/tty ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- /dev/tty }
}
} 2>/dev/null || (( _ghostty_fd = 1 ))
# Defer initialization so that other zsh init files can be configure
# the integration.
builtin typeset -ag precmd_functions
precmd_functions+=(_ghostty_deferred_init)
}
_ghostty_deferred_init() {
builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# Enable semantic markup with OSC 133.
_ghostty_precmd() {
builtin local -i cmd_status=$?
builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# Don't write OSC 133 D when our precmd handler is invoked from zle.
# Some plugins do that to update prompt on cd.
if ! builtin zle; then
# This code works incorrectly in the presence of a precmd or chpwd
# hook that prints. For example, sindresorhus/pure prints an empty
# line on precmd and marlonrichert/zsh-snap prints $PWD on chpwd.
# We'll end up writing our OSC 133 D mark too late.
#
# Another failure mode is when the output of a command doesn't end
# with LF and prompst_sp is set (it is by default). In this case
# we'll incorrectly state that '%' from prompt_sp is a part of the
# command's output.
if (( _ghostty_state == 1 )); then
# The last written OSC 133 C has not been closed with D yet.
# Close it and supply status.
builtin print -nu $_ghostty_fd '\e]133;D;'$cmd_status'\a'
(( _ghostty_state = 2 ))
elif (( _ghostty_state == 2 )); then
# There might be an unclosed OSC 133 C. Close that.
builtin print -nu $_ghostty_fd '\e]133;D\a'
fi
fi
builtin local mark1=$'%{\e]133;A;cl=line\a%}'
if [[ -o prompt_percent ]]; then
builtin typeset -g precmd_functions
if [[ ${precmd_functions[-1]} == _ghostty_precmd ]]; then
# This is the best case for us: we can add our marks to PS1 and
# PS2. This way our marks will be printed whenever zsh
# redisplays prompt: on reset-prompt, on SIGWINCH, and on
# SIGCHLD if notify is set. Themes that update prompt
# asynchronously from a `zle -F` handler might still remove our
# marks. Oh well.
# Restore PS1/PS2 to their pre-mark state if nothing else has
# modified them since we last added marks. This avoids exposing
# PS1 with our marks to other hooks (which can break themes like
# Pure that use pattern matching to strip/rebuild the prompt).
# If PS1 was modified (by a theme, async update, etc.), we
# keep the modified version, prioritizing the theme's changes.
builtin local ps1_changed=0
if [[ -n ${_ghostty_saved_ps1+x} ]]; then
if [[ $PS1 == $_ghostty_marked_ps1 ]]; then
PS1=$_ghostty_saved_ps1
PS2=$_ghostty_saved_ps2
elif [[ $PS1 != $_ghostty_saved_ps1 ]]; then
ps1_changed=1
fi
fi
# Save the clean PS1/PS2 before we add marks.
_ghostty_saved_ps1=$PS1
_ghostty_saved_ps2=$PS2
# Add our marks. Since we always start from a clean PS1
# (either restored above or freshly set by a theme), we can
# unconditionally add mark1 and markB.
builtin local mark2=$'%{\e]133;P;k=s\a%}'
builtin local markB=$'%{\e]133;B\a%}'
# If PS1 ends with a bare '%', it combines with the '{'
# in markB to form a '%{' prompt escape, swallowing the
# marker and producing a visible '{'. Fix by doubling the
# trailing '%' so it becomes a literal '%%'.
[[ $PS1 == *[^%]% || $PS1 == % ]] && PS1=$PS1%
PS1=${mark1}${PS1}${markB}
# Handle multiline prompts by marking newline-separated
# continuation lines with k=s (mark2).
#
# We skip this when PS1 changed because injecting marks into
# newlines can break pattern matching in themes that
# strip/rebuild the prompt dynamically (e.g., Pure).
if (( ! ps1_changed )) && [[ $PS1 == *$'\n'* ]]; then
PS1=${PS1//$'\n'/$'\n'${mark2}}
fi
# PS2 mark is needed when clearing the prompt on resize
[[ $PS2 == *[^%]% || $PS2 == % ]] && PS2=$PS2%
PS2=${mark2}${PS2}${markB}
# Save the marked PS1 so we can detect modifications
# by other hooks in the next cycle.
_ghostty_marked_ps1=$PS1
(( _ghostty_state = 2 ))
else
# If our precmd hook is not the last, we cannot rely on prompt
# changes to stick, so we don't even try. At least we can move
# our hook to the end to have better luck next time. If there is
# another piece of code that wants to take this privileged
# position, this won't work well. We'll break them as much as
# they are breaking us.
precmd_functions=(${precmd_functions:#_ghostty_precmd} _ghostty_precmd)
# Plugins that invoke precmd hooks from zle do that before zle
# is trashed. This means that the cursor is in the middle of
# BUFFER and we cannot print our mark there. Prompt might
# already have a mark, so the following reset-prompt will write
# it. If it doesn't, there is nothing we can do.
if ! builtin zle; then
builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
(( _ghostty_state = 2 ))
fi
fi
elif ! builtin zle; then
# Without prompt_percent we cannot patch prompt. Just print the
# mark, except when we are invoked from zle. In the latter case we
# cannot do anything.
builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
(( _ghostty_state = 2 ))
fi
}
_ghostty_preexec() {
builtin emulate -L zsh -o no_warn_create_global -o no_aliases
# Restore the original PS1/PS2 if nothing else has modified them
# since our precmd added marks. This ensures other preexec hooks
# see a clean PS1 without our marks. If PS1 was modified (e.g.,
# by an async theme update), we leave it alone.
if [[ -n ${_ghostty_saved_ps1+x} && $PS1 == $_ghostty_marked_ps1 ]]; then
PS1=$_ghostty_saved_ps1
PS2=$_ghostty_saved_ps2
fi
# This will work incorrectly in the presence of a preexec hook that
# prints. For example, if MichaelAquilina/zsh-you-should-use installs
# its preexec hook before us, we'll incorrectly mark its output as
# belonging to the command (as if the user typed it into zle) rather
# than command output.
builtin print -nu $_ghostty_fd '\e]133;C\a'
(( _ghostty_state = 1 ))
}
# Enable reporting current working dir to terminal. Ghostty supports
# the kitty-shell-cwd format.
_ghostty_report_pwd() { builtin print -nu $_ghostty_fd '\e]7;kitty-shell-cwd://'"$HOST""$PWD"'\a'; }
chpwd_functions=(${chpwd_functions[@]} "_ghostty_report_pwd")
# An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt
# as in this case chpwd_functions is insufficient. chpwd_functions is still needed for things like: cd x && something
functions[_ghostty_precmd]+="
_ghostty_report_pwd"
_ghostty_report_pwd
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
# Enable terminal title changes, formatted for user-friendly display.
functions[_ghostty_precmd]+="
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
functions[_ghostty_preexec]+="
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${1//[[:cntrl:]]}\"\$'\\a'"
fi
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
# Enable cursor shape changes depending on the current keymap.
# This implementation leaks blinking block cursor into external commands
# executed from zle. For example, users of fzf-based widgets may find
# themselves with a blinking block cursor within fzf.
_ghostty_zle_line_init _ghostty_zle_line_finish _ghostty_zle_keymap_select() {
builtin local steady=0
[[ "$GHOSTTY_SHELL_FEATURES" == *"cursor:steady"* ]] && steady=1
case ${KEYMAP-} in
vicmd|visual) builtin print -nu "$_ghostty_fd" "\e[$(( 1 + steady )) q" ;; # block
*) builtin print -nu "$_ghostty_fd" "\e[$(( 5 + steady )) q" ;; # bar
esac
}
# Restore the default shape before executing an external command
functions[_ghostty_preexec]+="
builtin print -rnu $_ghostty_fd \$'\\e[0 q'"
fi
# Emit semantic prompt markers at line-init if PS1 doesn't contain our
# marks. This ensures the terminal sees prompt markers even if another
# plugin (like zinit or oh-my-posh) regenerated PS1 after our precmd ran.
# We use 133;P instead of 133;A to avoid fresh-line behavior which would
# disrupt the display since the prompt has already been drawn. We also
# emit 133;B to mark the input area, which is needed for click-to-move.
(( $+functions[_ghostty_zle_line_init] )) || _ghostty_zle_line_init() { builtin true; }
functions[_ghostty_zle_line_init]="
if [[ \$PS1 != *$'%{\\e]133;A'* ]]; then
builtin print -nu \$_ghostty_fd '\\e]133;P;k=i\\a\\e]133;B\\a'
fi
"${functions[_ghostty_zle_line_init]}
# Add Ghostty binary to PATH if the path feature is enabled
if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* ]] && [[ -n "$GHOSTTY_BIN_DIR" ]]; then
if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then
builtin export PATH="$PATH:$GHOSTTY_BIN_DIR"
fi
fi
# Sudo
if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* ]] && [[ -n "$TERMINFO" ]]; then
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
function sudo() {
builtin local sudo_has_sudoedit_flags="no"
for arg in "$@"; do
# Check if argument is '-e' or '--edit' (sudoedit flags)
if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
sudo_has_sudoedit_flags="yes"
builtin break
fi
# Check if argument is neither an option nor a key-value pair
if [[ "$arg" != -* && "$arg" != *=* ]]; then
builtin break
fi
done
if [[ "$sudo_has_sudoedit_flags" == "yes" ]]; then
builtin command sudo "$@";
else
builtin command sudo --preserve-env=TERMINFO "$@";
fi
}
fi
# SSH Integration
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
function ssh() {
emulate -L zsh
setopt local_options no_glob_subst
local ssh_term ssh_opts
ssh_term="xterm-256color"
ssh_opts=()
# Configure environment variables for remote session
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-env* ]]; then
ssh_opts+=(-o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
fi
# Install terminfo on remote host if needed
if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-terminfo* ]]; then
local ssh_user ssh_hostname
while IFS=' ' read -r ssh_key ssh_value; do
case "$ssh_key" in
user) ssh_user="$ssh_value" ;;
hostname) ssh_hostname="$ssh_value" ;;
esac
[[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
done < <(command ssh -G "$@" 2>/dev/null)
if [[ -n "$ssh_hostname" ]]; then
local ssh_target="${ssh_user}@${ssh_hostname}"
# Check if terminfo is already cached
if "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1; then
ssh_term="xterm-ghostty"
elif (( $+commands[infocmp] )); then
local ssh_terminfo ssh_cpath_dir ssh_cpath
ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
if [[ -n "$ssh_terminfo" ]]; then
print "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
ssh_cpath="$ssh_cpath_dir/socket"
if builtin print -r "$ssh_terminfo" | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null; then
ssh_term="xterm-ghostty"
ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation
"$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
else
print "Warning: Failed to install terminfo." >&2
fi
else
print "Warning: Could not generate terminfo data." >&2
fi
else
print "Warning: ghostty command not available for cache management." >&2
fi
fi
fi
# Execute SSH with TERM environment variable
TERM="$ssh_term" COLORTERM=truecolor command ssh "${ssh_opts[@]}" "$@"
}
fi
# Some zsh users manually run `source ~/.zshrc` in order to apply rc file
# changes to the current shell. This is a terrible practice that breaks many
# things, including our shell integration. For example, Oh My Zsh and Prezto
# (both very popular among zsh users) will remove zle-line-init and
# zle-line-finish hooks if .zshrc is manually sourced. Prezto will also remove
# zle-keymap-select.
#
# Another common (and much more robust) way to apply rc file changes to the
# current shell is `exec zsh`. This will remove our integration from the shell
# unless it's explicitly invoked from .zshrc. This is not an issue with
# `exec zsh` but rather with our implementation of automatic shell integration.
# In the ideal world we would use add-zle-hook-widget to hook zle-line-init
# and similar widget. This breaks user configs though, so we have do this
# horrible thing instead.
builtin local hook func widget orig_widget flag
for hook in line-init line-finish keymap-select; do
func=_ghostty_zle_${hook/-/_}
(( $+functions[$func] )) || builtin continue
widget=zle-$hook
if [[ $widgets[$widget] == user:azhw:* &&
$+functions[add-zle-hook-widget] -eq 1 ]]; then
# If the widget is already hooked by add-zle-hook-widget at the top
# level, add our hook at the end. We MUST do it this way. We cannot
# just wrap the widget ourselves in this case because it would
# trigger bugs in add-zle-hook-widget.
add-zle-hook-widget $hook $func
else
if (( $+widgets[$widget] )); then
# There is a widget but it's not from add-zle-hook-widget. We
# can rename the original widget, install our own and invoke
# the original when we are called.
#
# Note: The leading dot is to work around bugs in
# zsh-syntax-highlighting.
orig_widget=._ghostty_orig_$widget
builtin zle -A $widget $orig_widget
if [[ $widgets[$widget] == user:* ]]; then
# No -w here to preserve $WIDGET within the original widget.
flag=
else
flag=w
fi
functions[$func]+="
builtin zle $orig_widget -N$flag -- \"\$@\""
fi
builtin zle -N $widget $func
fi
done
if (( $+functions[_ghostty_preexec] )); then
builtin typeset -ag preexec_functions
preexec_functions+=(_ghostty_preexec)
fi
builtin typeset -ag precmd_functions
if (( $+functions[_ghostty_precmd] )); then
precmd_functions=(${precmd_functions:#_ghostty_deferred_init} _ghostty_precmd)
_ghostty_precmd
else
precmd_functions=(${precmd_functions:#_ghostty_deferred_init})
fi
# Unfunction _ghostty_deferred_init to save memory. Don't unfunction
# ghostty-integration though because decent public functions aren't supposed to
# to unfunction themselves when invoked. Unfunctioning is done by calling code.
builtin unfunction _ghostty_deferred_init
}
_entrypoint

View File

@@ -0,0 +1,22 @@
palette = 0=#262427
palette = 1=#ff666d
palette = 2=#b3e03a
palette = 3=#ffc739
palette = 4=#00cde8
palette = 5=#a392e8
palette = 6=#9deaf6
palette = 7=#fcfcfa
palette = 8=#545452
palette = 9=#ff7e83
palette = 10=#bee55e
palette = 11=#ffd05e
palette = 12=#1bd5eb
palette = 13=#b0a3eb
palette = 14=#acedf8
palette = 15=#fcfcfa
background = #262427
foreground = #fcfcfa
cursor-color = #fcfcfa
cursor-text = #000000
selection-background = #fcfcfa
selection-foreground = #262427

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#a03050
palette = 2=#40d080
palette = 3=#e09040
palette = 4=#3060b0
palette = 5=#603090
palette = 6=#0090c0
palette = 7=#dbded8
palette = 8=#685656
palette = 9=#c06060
palette = 10=#90d050
palette = 11=#e0d000
palette = 12=#00b0c0
palette = 13=#801070
palette = 14=#20b0c0
palette = 15=#ffffff
background = #040404
foreground = #feffff
cursor-color = #e0d000
cursor-text = #000000
selection-background = #606060
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#090300
palette = 1=#db2d20
palette = 2=#01a252
palette = 3=#caba00
palette = 4=#01a0e4
palette = 5=#a16a94
palette = 6=#8fbece
palette = 7=#a5a2a2
palette = 8=#5c5855
palette = 9=#dbaec3
palette = 10=#3a3432
palette = 11=#4a4543
palette = 12=#807d7c
palette = 13=#bcbbba
palette = 14=#cdab53
palette = 15=#f7f7f7
background = #f7f7f7
foreground = #4a4543
cursor-color = #4a4543
cursor-text = #f7f7f7
selection-background = #a5a2a2
selection-foreground = #4a4543

View File

@@ -0,0 +1,22 @@
palette = 0=#090300
palette = 1=#db2d20
palette = 2=#01a252
palette = 3=#fded02
palette = 4=#01a0e4
palette = 5=#a16a94
palette = 6=#b5e4f4
palette = 7=#a5a2a2
palette = 8=#5c5855
palette = 9=#e8bbd0
palette = 10=#47413f
palette = 11=#4a4543
palette = 12=#807d7c
palette = 13=#d6d5d4
palette = 14=#cdab53
palette = 15=#f7f7f7
background = #090300
foreground = #a5a2a2
cursor-color = #a5a2a2
cursor-text = #090300
selection-background = #4a4543
selection-foreground = #a5a2a2

View File

@@ -0,0 +1,22 @@
palette = 0=#191919
palette = 1=#aa342e
palette = 2=#4b8c0f
palette = 3=#dbba00
palette = 4=#1370d3
palette = 5=#c43ac3
palette = 6=#008eb0
palette = 7=#bebebe
palette = 8=#525252
palette = 9=#f05b50
palette = 10=#95dc55
palette = 11=#ffe763
palette = 12=#60a4ec
palette = 13=#e26be2
palette = 14=#60b6cb
palette = 15=#f7f7f7
background = #102040
foreground = #dddddd
cursor-color = #007acc
cursor-text = #bfdbfe
selection-background = #bfdbfe
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#cd0000
palette = 2=#00cd00
palette = 3=#cdcd00
palette = 4=#1093f5
palette = 5=#cd00cd
palette = 6=#00cdcd
palette = 7=#faebd7
palette = 8=#404040
palette = 9=#ff0000
palette = 10=#00ff00
palette = 11=#ffff00
palette = 12=#11b5f6
palette = 13=#ff00ff
palette = 14=#00ffff
palette = 15=#ffffff
background = #111416
foreground = #eeeeec
cursor-color = #bbbbbb
cursor-text = #ffffff
selection-background = #eeeeec
selection-foreground = #333333

View File

@@ -0,0 +1,22 @@
palette = 0=#040404
palette = 1=#d84a33
palette = 2=#5da602
palette = 3=#eebb6e
palette = 4=#417ab3
palette = 5=#e5c499
palette = 6=#bdcfe5
palette = 7=#dbded8
palette = 8=#685656
palette = 9=#d76b42
palette = 10=#99b52c
palette = 11=#ffb670
palette = 12=#97d7ef
palette = 13=#aa7900
palette = 14=#bdcfe5
palette = 15=#e4d5c7
background = #040404
foreground = #feffff
cursor-color = #feffff
cursor-text = #000000
selection-background = #606060
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#050404
palette = 1=#bd0013
palette = 2=#4ab118
palette = 3=#e7741e
palette = 4=#0f4ac6
palette = 5=#665993
palette = 6=#70a598
palette = 7=#f8dcc0
palette = 8=#4e7cbf
palette = 9=#fc5f5a
palette = 10=#9eff6e
palette = 11=#efc11a
palette = 12=#1997c6
palette = 13=#9b5953
palette = 14=#c8faf4
palette = 15=#f6f5fb
background = #1f1d45
foreground = #f8dcc0
cursor-color = #efbf38
cursor-text = #08080a
selection-background = #706b4e
selection-foreground = #f3d9c4

View File

@@ -0,0 +1,22 @@
palette = 0=#241f31
palette = 1=#c01c28
palette = 2=#2ec27e
palette = 3=#e8b504
palette = 4=#1e78e4
palette = 5=#9841bb
palette = 6=#0ab9dc
palette = 7=#c0bfbc
palette = 8=#5e5c64
palette = 9=#ed333b
palette = 10=#4ad67c
palette = 11=#d2be36
palette = 12=#51a1ff
palette = 13=#c061cb
palette = 14=#4fd2fd
palette = 15=#f6f5f4
background = #ffffff
foreground = #000000
cursor-color = #000000
cursor-text = #ffffff
selection-background = #c0bfbc
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#241f31
palette = 1=#c01c28
palette = 2=#2ec27e
palette = 3=#f5c211
palette = 4=#1e78e4
palette = 5=#9841bb
palette = 6=#0ab9dc
palette = 7=#c0bfbc
palette = 8=#5e5c64
palette = 9=#ed333b
palette = 10=#57e389
palette = 11=#f8e45c
palette = 12=#51a1ff
palette = 13=#c061cb
palette = 14=#4fd2fd
palette = 15=#f6f5f4
background = #1d1d20
foreground = #ffffff
cursor-color = #ffffff
cursor-text = #1d1d20
selection-background = #ffffff
selection-foreground = #5e5c64

View File

@@ -0,0 +1,22 @@
palette = 0=#151515
palette = 1=#ac4142
palette = 2=#7e8e50
palette = 3=#e5b567
palette = 4=#6c99bb
palette = 5=#9f4e85
palette = 6=#7dd6cf
palette = 7=#d0d0d0
palette = 8=#505050
palette = 9=#ac4142
palette = 10=#7e8e50
palette = 11=#e5b567
palette = 12=#6c99bb
palette = 13=#9f4e85
palette = 14=#7dd6cf
palette = 15=#f5f5f5
background = #212121
foreground = #d0d0d0
cursor-color = #d0d0d0
cursor-text = #151515
selection-background = #303030
selection-foreground = #d0d0d0

View File

@@ -0,0 +1,22 @@
palette = 0=#1a1a1a
palette = 1=#f08898
palette = 2=#a4e09c
palette = 3=#f5dea4
palette = 4=#84b4f8
palette = 5=#c8a2f4
palette = 6=#90dcd0
palette = 7=#d0d6f0
palette = 8=#444444
palette = 9=#f08898
palette = 10=#a4e09c
palette = 11=#f5dea4
palette = 12=#84b4f8
palette = 13=#c8a2f4
palette = 14=#90dcd0
palette = 15=#ffffff
background = #1a1a1a
foreground = #d0d6f0
cursor-color = #f8b080
cursor-text = #1a1a1a
selection-background = #333333
selection-foreground = #d0d6f0

View File

@@ -0,0 +1,22 @@
palette = 0=#f0f2f6
palette = 1=#d00c36
palette = 2=#3e9e28
palette = 3=#dd8c1a
palette = 4=#1c64f2
palette = 5=#8636ec
palette = 6=#159096
palette = 7=#4a4d66
palette = 8=#adb2bc
palette = 9=#d00c36
palette = 10=#3e9e28
palette = 11=#dd8c1a
palette = 12=#1c64f2
palette = 13=#8636ec
palette = 14=#159096
palette = 15=#4a4d66
background = #f0f2f6
foreground = #4a4d66
cursor-color = #fc6008
cursor-text = #f0f2f6
selection-background = #bdc2cc
selection-foreground = #4a4d66

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#aa3731
palette = 2=#448c27
palette = 3=#cb9000
palette = 4=#325cc0
palette = 5=#7a3e9d
palette = 6=#0083b2
palette = 7=#b7b7b7
palette = 8=#777777
palette = 9=#f05050
palette = 10=#60cb00
palette = 11=#f2af50
palette = 12=#007acc
palette = 13=#e64ce6
palette = 14=#00aacb
palette = 15=#f7f7f7
background = #f7f7f7
foreground = #000000
cursor-color = #007acc
cursor-text = #bfdbfe
selection-background = #bfdbfe
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#112616
palette = 1=#7f2b27
palette = 2=#2f7e25
palette = 3=#717f24
palette = 4=#2f6a7f
palette = 5=#47587f
palette = 6=#327f77
palette = 7=#647d75
palette = 8=#3c4812
palette = 9=#e08009
palette = 10=#18e000
palette = 11=#bde000
palette = 12=#00aae0
palette = 13=#0058e0
palette = 14=#00e0c4
palette = 15=#73fa91
background = #0f1610
foreground = #637d75
cursor-color = #73fa91
cursor-text = #0f1610
selection-background = #1d4125
selection-foreground = #73fa91

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#cd3131
palette = 2=#05bc79
palette = 3=#e5e512
palette = 4=#2472c8
palette = 5=#bc3fbc
palette = 6=#0fa8cd
palette = 7=#e5e5e5
palette = 8=#666666
palette = 9=#cd3131
palette = 10=#05bc79
palette = 11=#e5e512
palette = 12=#2472c8
palette = 13=#bc3fbc
palette = 14=#0fa8cd
palette = 15=#e5e5e5
background = #262a33
foreground = #e5e5e5
cursor-color = #f8f8f0
cursor-text = #b5b5a8
selection-background = #5a5c62
selection-foreground = #ece7e7

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#c91b00
palette = 2=#00c200
palette = 3=#c7c400
palette = 4=#1c3fe1
palette = 5=#ca30c7
palette = 6=#00c5c7
palette = 7=#c7c7c7
palette = 8=#686868
palette = 9=#ff6e67
palette = 10=#5ffa68
palette = 11=#fffc67
palette = 12=#6871ff
palette = 13=#ff77ff
palette = 14=#60fdff
palette = 15=#ffffff
background = #2c2b2b
foreground = #d5a200
cursor-color = #c7c7c7
cursor-text = #8c8c8c
selection-background = #6b5b02
selection-foreground = #67e000

View File

@@ -0,0 +1,22 @@
palette = 0=#1a1a1a
palette = 1=#cc372e
palette = 2=#26a439
palette = 3=#cdac08
palette = 4=#0869cb
palette = 5=#9647bf
palette = 6=#479ec2
palette = 7=#98989d
palette = 8=#464646
palette = 9=#ff453a
palette = 10=#32d74b
palette = 11=#ffd60a
palette = 12=#0a84ff
palette = 13=#bf5af2
palette = 14=#76d6ff
palette = 15=#ffffff
background = #1e1e1e
foreground = #ffffff
cursor-color = #98989d
cursor-text = #ffffff
selection-background = #3f638b
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#1a1a1a
palette = 1=#cc372e
palette = 2=#26a439
palette = 3=#cdac08
palette = 4=#0869cb
palette = 5=#9647bf
palette = 6=#479ec2
palette = 7=#98989d
palette = 8=#464646
palette = 9=#ff453a
palette = 10=#32d74b
palette = 11=#e5bc00
palette = 12=#0a84ff
palette = 13=#bf5af2
palette = 14=#69c9f2
palette = 15=#ffffff
background = #feffff
foreground = #000000
cursor-color = #98989d
cursor-text = #ffffff
selection-background = #abd8ff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#333333
palette = 1=#da2700
palette = 2=#12c258
palette = 3=#ffc656
palette = 4=#518bfc
palette = 5=#e37bd9
palette = 6=#63fad5
palette = 7=#bab2b2
palette = 8=#777777
palette = 9=#ffb9b9
palette = 10=#e3f6aa
palette = 11=#ffddaa
palette = 12=#b3e8f3
palette = 13=#cbbaf9
palette = 14=#bcffc7
palette = 15=#efefef
background = #201f1e
foreground = #eee4d9
cursor-color = #872929
cursor-text = #fffbf2
selection-background = #25524a
selection-foreground = #f3fffd

View File

@@ -0,0 +1,22 @@
palette = 0=#2c2c2c
palette = 1=#d3322d
palette = 2=#588b35
palette = 3=#fca93a
palette = 4=#2465c2
palette = 5=#7332b4
palette = 6=#64e1b8
palette = 7=#f7f7f7
palette = 8=#535353
palette = 9=#fa5852
palette = 10=#8dc252
palette = 11=#ffea51
palette = 12=#6ab5f8
palette = 13=#be68ca
palette = 14=#89ffdb
palette = 15=#fefefe
background = #1e1e1e
foreground = #eaeaea
cursor-color = #f7f7f7
cursor-text = #000000
selection-background = #46515e
selection-foreground = #f1f3f5

View File

@@ -0,0 +1,22 @@
palette = 0=#232323
palette = 1=#ff000f
palette = 2=#8ce10b
palette = 3=#ffb900
palette = 4=#008df8
palette = 5=#6d43a6
palette = 6=#00d8eb
palette = 7=#ffffff
palette = 8=#444444
palette = 9=#ff2740
palette = 10=#abe15b
palette = 11=#ffd242
palette = 12=#0092ff
palette = 13=#9a5feb
palette = 14=#67fff0
palette = 15=#ffffff
background = #0e1019
foreground = #fffaf4
cursor-color = #ff0018
cursor-text = #ff8ca4
selection-background = #002a3b
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#3d352a
palette = 1=#cd5c5c
palette = 2=#86af80
palette = 3=#e8ae5b
palette = 4=#6495ed
palette = 5=#deb887
palette = 6=#b0c4de
palette = 7=#bbaa99
palette = 8=#554444
palette = 9=#cc5533
palette = 10=#88aa22
palette = 11=#ffa75d
palette = 12=#87ceeb
palette = 13=#996600
palette = 14=#b0c4de
palette = 15=#ddccbb
background = #1c1c1c
foreground = #ddeedd
cursor-color = #e2bbef
cursor-text = #000000
selection-background = #4d4d4d
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#202746
palette = 1=#c94922
palette = 2=#ac9739
palette = 3=#c08b30
palette = 4=#3d8fd1
palette = 5=#6679cc
palette = 6=#22a2c9
palette = 7=#979db4
palette = 8=#6b7394
palette = 9=#c76b29
palette = 10=#4f587c
palette = 11=#5e6687
palette = 12=#898ea4
palette = 13=#dfe2f1
palette = 14=#9c637a
palette = 15=#f5f7ff
background = #202746
foreground = #979db4
cursor-color = #979db4
cursor-text = #202746
selection-background = #5e6687
selection-foreground = #979db4

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#fd5ff1
palette = 2=#87c38a
palette = 3=#ffd7b1
palette = 4=#85befd
palette = 5=#b9b6fc
palette = 6=#85befd
palette = 7=#e0e0e0
palette = 8=#4c4c4c
palette = 9=#fd5ff1
palette = 10=#94fa36
palette = 11=#f5ffa8
palette = 12=#96cbfe
palette = 13=#b9b6fc
palette = 14=#85befd
palette = 15=#e0e0e0
background = #161719
foreground = #c5c8c6
cursor-color = #d0d0d0
cursor-text = #151515
selection-background = #444444
selection-foreground = #c5c8c6

View File

@@ -0,0 +1,22 @@
palette = 0=#21252b
palette = 1=#e06c75
palette = 2=#98c379
palette = 3=#e5c07b
palette = 4=#61afef
palette = 5=#c678dd
palette = 6=#56b6c2
palette = 7=#abb2bf
palette = 8=#767676
palette = 9=#e06c75
palette = 10=#98c379
palette = 11=#e5c07b
palette = 12=#61afef
palette = 13=#c678dd
palette = 14=#56b6c2
palette = 15=#abb2bf
background = #21252b
foreground = #abb2bf
cursor-color = #abb2bf
cursor-text = #21252b
selection-background = #323844
selection-foreground = #abb2bf

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#de3e35
palette = 2=#3f953a
palette = 3=#d2b67c
palette = 4=#2f5af3
palette = 5=#950095
palette = 6=#3f953a
palette = 7=#bbbbbb
palette = 8=#000000
palette = 9=#de3e35
palette = 10=#3f953a
palette = 11=#d2b67c
palette = 12=#2f5af3
palette = 13=#a00095
palette = 14=#3f953a
palette = 15=#ffffff
background = #f9f9f9
foreground = #2a2c33
cursor-color = #bbbbbb
cursor-text = #ffffff
selection-background = #ededed
selection-foreground = #2a2c33

View File

@@ -0,0 +1,22 @@
palette = 0=#110f18
palette = 1=#ff6767
palette = 2=#61ffca
palette = 3=#ffca85
palette = 4=#a277ff
palette = 5=#a277ff
palette = 6=#61ffca
palette = 7=#edecee
palette = 8=#4d4d4d
palette = 9=#ffca85
palette = 10=#a277ff
palette = 11=#ffca85
palette = 12=#a277ff
palette = 13=#a277ff
palette = 14=#61ffca
palette = 15=#edecee
background = #15141b
foreground = #edecee
cursor-color = #a277ff
cursor-text = #edecee
selection-background = #a277ff
selection-foreground = #edecee

View File

@@ -0,0 +1,22 @@
palette = 0=#23262e
palette = 1=#f0266f
palette = 2=#8fd46d
palette = 3=#ffe66d
palette = 4=#102ee4
palette = 5=#ee5d43
palette = 6=#03d6b8
palette = 7=#c74ded
palette = 8=#4f545e
palette = 9=#f92672
palette = 10=#8fd46d
palette = 11=#ffe66d
palette = 12=#03d6b8
palette = 13=#ee5d43
palette = 14=#03d6b8
palette = 15=#c74ded
background = #23262e
foreground = #ffca28
cursor-color = #ee5d43
cursor-text = #ffd29c
selection-background = #292e38
selection-foreground = #00e8c6

View File

@@ -0,0 +1,22 @@
palette = 0=#11151c
palette = 1=#ea6c73
palette = 2=#7fd962
palette = 3=#f9af4f
palette = 4=#53bdfa
palette = 5=#cda1fa
palette = 6=#90e1c6
palette = 7=#c7c7c7
palette = 8=#686868
palette = 9=#f07178
palette = 10=#aad94c
palette = 11=#ffb454
palette = 12=#59c2ff
palette = 13=#d2a6ff
palette = 14=#95e6cb
palette = 15=#ffffff
background = #0b0e14
foreground = #bfbdb6
cursor-color = #e6b450
cursor-text = #0b0e14
selection-background = #409fff
selection-foreground = #0b0e14

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#ea6c6d
palette = 2=#6cbf43
palette = 3=#eca944
palette = 4=#3199e1
palette = 5=#9e75c7
palette = 6=#46ba94
palette = 7=#bababa
palette = 8=#686868
palette = 9=#f07171
palette = 10=#86b300
palette = 11=#f2ae49
palette = 12=#399ee6
palette = 13=#a37acc
palette = 14=#4cbf99
palette = 15=#d1d1d1
background = #f8f9fa
foreground = #5c6166
cursor-color = #ffaa33
cursor-text = #f8f9fa
selection-background = #035bd6
selection-foreground = #f8f9fa

View File

@@ -0,0 +1,22 @@
palette = 0=#171b24
palette = 1=#ed8274
palette = 2=#87d96c
palette = 3=#facc6e
palette = 4=#6dcbfa
palette = 5=#dabafa
palette = 6=#90e1c6
palette = 7=#c7c7c7
palette = 8=#686868
palette = 9=#f28779
palette = 10=#d5ff80
palette = 11=#ffd173
palette = 12=#73d0ff
palette = 13=#dfbfff
palette = 14=#95e6cb
palette = 15=#ffffff
background = #1f2430
foreground = #cccac2
cursor-color = #ffcc66
cursor-text = #1f2430
selection-background = #409fff
selection-foreground = #1f2430

View File

@@ -0,0 +1,22 @@
palette = 0=#17141f
palette = 1=#ff6b7f
palette = 2=#00bd9c
palette = 3=#e6c62f
palette = 4=#22e8df
palette = 5=#dc396a
palette = 6=#56b6c2
palette = 7=#f1f1f1
palette = 8=#495162
palette = 9=#fe9ea1
palette = 10=#98c379
palette = 11=#f9e46b
palette = 12=#91fff4
palette = 13=#da70d6
palette = 14=#bcf3ff
palette = 15=#ffffff
background = #191323
foreground = #cccccc
cursor-color = #e07d13
cursor-text = #ffffff
selection-background = #220525
selection-foreground = #f4f4f4

View File

@@ -0,0 +1,22 @@
palette = 0=#1b1d1e
palette = 1=#e6dc44
palette = 2=#c8be46
palette = 3=#f4fd22
palette = 4=#737174
palette = 5=#747271
palette = 6=#62605f
palette = 7=#c6c5bf
palette = 8=#505354
palette = 9=#fff78e
palette = 10=#fff27d
palette = 11=#feed6c
palette = 12=#919495
palette = 13=#9a9a9d
palette = 14=#a3a3a6
palette = 15=#dadbd6
background = #1b1d1e
foreground = #6f6f6f
cursor-color = #fcef0c
cursor-text = #000000
selection-background = #4d504c
selection-foreground = #f0e04a

View File

@@ -0,0 +1,22 @@
palette = 0=#20111b
palette = 1=#be100e
palette = 2=#858162
palette = 3=#d08b30
palette = 4=#426a79
palette = 5=#97522c
palette = 6=#989a9c
palette = 7=#968c83
palette = 8=#5e5252
palette = 9=#be100e
palette = 10=#858162
palette = 11=#d08b30
palette = 12=#426a79
palette = 13=#97522c
palette = 14=#989a9c
palette = 15=#d5ccba
background = #d5ccba
foreground = #45373c
cursor-color = #45373c
cursor-text = #d5ccba
selection-background = #968c83
selection-foreground = #45373c

View File

@@ -0,0 +1,22 @@
palette = 0=#20111b
palette = 1=#be100e
palette = 2=#858162
palette = 3=#eaa549
palette = 4=#426a79
palette = 5=#97522c
palette = 6=#989a9c
palette = 7=#968c83
palette = 8=#5e5252
palette = 9=#be100e
palette = 10=#858162
palette = 11=#eaa549
palette = 12=#426a79
palette = 13=#97522c
palette = 14=#989a9c
palette = 15=#d5ccba
background = #20111b
foreground = #968c83
cursor-color = #968c83
cursor-text = #20111b
selection-background = #45373c
selection-foreground = #968c83

View File

@@ -0,0 +1,22 @@
palette = 0=#573d26
palette = 1=#be2d26
palette = 2=#6ba18a
palette = 3=#e99d2a
palette = 4=#5a86ad
palette = 5=#ac80a6
palette = 6=#74a6ad
palette = 7=#e0dbb7
palette = 8=#9b6c4a
palette = 9=#e84627
palette = 10=#95d8ba
palette = 11=#d0d150
palette = 12=#b8d3ed
palette = 13=#d19ecb
palette = 14=#93cfd7
palette = 15=#fff9d5
background = #2a1f1d
foreground = #e0dbb7
cursor-color = #644a33
cursor-text = #8a7059
selection-background = #563c27
selection-foreground = #e0dbbb

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#486e6f
palette = 2=#dd9999
palette = 3=#a06666
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#486e6f
palette = 10=#dd9999
palette = 11=#a06666
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#fbcb97
palette = 3=#e78a53
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#fbcb97
palette = 11=#e78a53
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#ddeecc
palette = 3=#99bbaa
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#ddeecc
palette = 11=#99bbaa
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#d0dfee
palette = 3=#5f81a5
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#d0dfee
palette = 11=#5f81a5
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#9b8d7f
palette = 3=#8c7f70
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#9b8d7f
palette = 11=#8c7f70
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#7799bb
palette = 3=#556677
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#7799bb
palette = 11=#556677
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#eceee3
palette = 3=#974b46
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#eceee3
palette = 11=#974b46
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#a5aaa7
palette = 3=#626b67
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#a5aaa7
palette = 11=#626b67
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#f3ecd4
palette = 3=#eecc6c
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#f3ecd4
palette = 11=#eecc6c
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#aa9988
palette = 3=#777755
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#aa9988
palette = 11=#777755
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#5f8787
palette = 2=#f8f7f2
palette = 3=#79241f
palette = 4=#888888
palette = 5=#999999
palette = 6=#aaaaaa
palette = 7=#c1c1c1
palette = 8=#404040
palette = 9=#5f8787
palette = 10=#f8f7f2
palette = 11=#79241f
palette = 12=#888888
palette = 13=#999999
palette = 14=#aaaaaa
palette = 15=#c1c1c1
background = #000000
foreground = #c1c1c1
cursor-color = #c1c1c1
cursor-text = #8e8e8e
selection-background = #c1c1c1
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#b87a7a
palette = 2=#7ab87a
palette = 3=#b8b87a
palette = 4=#7a7ab8
palette = 5=#b87ab8
palette = 6=#7ab8b8
palette = 7=#d9d9d9
palette = 8=#4c4c4c
palette = 9=#dbbdbd
palette = 10=#bddbbd
palette = 11=#dbdbbd
palette = 12=#bdbddb
palette = 13=#dbbddb
palette = 14=#bddbdb
palette = 15=#ffffff
background = #0d1926
foreground = #d9e6f2
cursor-color = #d9e6f2
cursor-text = #0d1926
selection-background = #c1ddff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#0a4c62
palette = 1=#99246e
palette = 2=#5cb1b3
palette = 3=#eab9a8
palette = 4=#90a5bd
palette = 5=#9d54a7
palette = 6=#7e83cc
palette = 7=#f0e8d6
palette = 8=#463c5d
palette = 9=#c87272
palette = 10=#0a6c7e
palette = 11=#7a3188
palette = 12=#5f3d63
palette = 13=#bc94b7
palette = 14=#5e6071
palette = 15=#0a6c7e
background = #1c0c28
foreground = #babab9
cursor-color = #fcfad6
cursor-text = #000000
selection-background = #606060
selection-foreground = #ffffff

View File

@@ -0,0 +1,22 @@
palette = 0=#292d3e
palette = 1=#ff8288
palette = 2=#b4e88d
palette = 3=#f4d69f
palette = 4=#82aaff
palette = 5=#e9c1ff
palette = 6=#89ebff
palette = 7=#d0d0d0
palette = 8=#9094a4
palette = 9=#ff8b92
palette = 10=#ddffa7
palette = 11=#ffe585
palette = 12=#9cc4ff
palette = 13=#ddb0f6
palette = 14=#a3f7ff
palette = 15=#ffffff
background = #006984
foreground = #c5f2ff
cursor-color = #ffcc00
cursor-text = #292d3e
selection-background = #2baeca
selection-foreground = #eceff1

View File

@@ -0,0 +1,22 @@
palette = 0=#101116
palette = 1=#ff5680
palette = 2=#00ff9c
palette = 3=#fffc58
palette = 4=#00b0ff
palette = 5=#d57bff
palette = 6=#76c1ff
palette = 7=#c7c7c7
palette = 8=#686868
palette = 9=#ff6e67
palette = 10=#5ffa68
palette = 11=#fffc67
palette = 12=#6871ff
palette = 13=#d682ec
palette = 14=#60fdff
palette = 15=#ffffff
background = #101116
foreground = #00a2ff
cursor-color = #76ff9f
cursor-text = #a6a6a6
selection-background = #c1deff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#41444d
palette = 1=#fc2f52
palette = 2=#25a45c
palette = 3=#ff936a
palette = 4=#3476ff
palette = 5=#7a82da
palette = 6=#4483aa
palette = 7=#cdd4e0
palette = 8=#8f9aae
palette = 9=#ff6480
palette = 10=#3fc56b
palette = 11=#f9c859
palette = 12=#10b1fe
palette = 13=#ff78f8
palette = 14=#5fb9bc
palette = 15=#ffffff
background = #282c34
foreground = #b9c0cb
cursor-color = #ffcc00
cursor-text = #282c34
selection-background = #b9c0ca
selection-foreground = #272b33

View File

@@ -0,0 +1,22 @@
palette = 0=#373a41
palette = 1=#d52753
palette = 2=#23974a
palette = 3=#df631c
palette = 4=#275fe4
palette = 5=#823ff1
palette = 6=#27618d
palette = 7=#babbc2
palette = 8=#676a77
palette = 9=#ff6480
palette = 10=#3cbc66
palette = 11=#c5a332
palette = 12=#0099e1
palette = 13=#ce33c0
palette = 14=#6d93bb
palette = 15=#d3d3d3
background = #f9f9f9
foreground = #373a41
cursor-color = #f32759
cursor-text = #ffffff
selection-background = #daf0ff
selection-foreground = #373a41

View File

@@ -0,0 +1,22 @@
palette = 0=#4f4f4f
palette = 1=#ff6c60
palette = 2=#a8ff60
palette = 3=#ffffb6
palette = 4=#96cbfe
palette = 5=#ff73fd
palette = 6=#c6c5fe
palette = 7=#eeeeee
palette = 8=#7c7c7c
palette = 9=#ffb6b0
palette = 10=#ceffac
palette = 11=#ffffcc
palette = 12=#b5dcff
palette = 13=#ff9cfe
palette = 14=#dfdffe
palette = 15=#ffffff
background = #0000a4
foreground = #ffff4e
cursor-color = #ffa560
cursor-text = #ffffff
selection-background = #a4a4a4
selection-foreground = #0000a4

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#cc0403
palette = 2=#19cb00
palette = 3=#cecb00
palette = 4=#0d73cc
palette = 5=#cb1ed1
palette = 6=#0dcdcd
palette = 7=#dddddd
palette = 8=#767676
palette = 9=#f2201f
palette = 10=#23fd00
palette = 11=#fffd00
palette = 12=#1a8fff
palette = 13=#fd28ff
palette = 14=#14ffff
palette = 15=#ffffff
background = #141d2b
foreground = #9fef00
cursor-color = #9fef00
cursor-text = #111111
selection-background = #a4b1cd
selection-foreground = #141d2b

View File

@@ -0,0 +1,22 @@
palette = 0=#362c24
palette = 1=#b10b00
palette = 2=#007232
palette = 3=#8b4c00
palette = 4=#005cb4
palette = 5=#9b0097
palette = 6=#006a78
palette = 7=#baa99d
palette = 8=#514337
palette = 9=#de1100
palette = 10=#008f40
palette = 11=#ae6000
palette = 12=#0074e1
palette = 13=#c300bd
palette = 14=#008697
palette = 15=#eae1da
background = #f1ebe6
foreground = #362c24
cursor-color = #362c24
cursor-text = #f1ebe6
selection-background = #362c24
selection-foreground = #f1ebe6

View File

@@ -0,0 +1,22 @@
palette = 0=#31363b
palette = 1=#ed1515
palette = 2=#11d116
palette = 3=#f67400
palette = 4=#1d99f3
palette = 5=#9b59b6
palette = 6=#1abc9c
palette = 7=#eff0f1
palette = 8=#7f8c8d
palette = 9=#c0392b
palette = 10=#1cdc9a
palette = 11=#fdbc4b
palette = 12=#3daee9
palette = 13=#8e44ad
palette = 14=#16a085
palette = 15=#fcfcfc
background = #31363b
foreground = #eff0f1
cursor-color = #eff0f1
cursor-text = #31363b
selection-background = #eff0f1
selection-foreground = #31363b

View File

@@ -0,0 +1,22 @@
palette = 0=#191919
palette = 1=#ff355b
palette = 2=#b7e876
palette = 3=#ffc251
palette = 4=#76d4ff
palette = 5=#ba76e7
palette = 6=#6cbfb5
palette = 7=#c2c8d7
palette = 8=#4c4c4c
palette = 9=#ff355b
palette = 10=#b7e876
palette = 11=#ffc251
palette = 12=#76d5ff
palette = 13=#ba76e7
palette = 14=#6cbfb5
palette = 15=#c2c8d7
background = #191919
foreground = #b3c9d7
cursor-color = #f34b00
cursor-text = #002831
selection-background = #b3c9d7
selection-foreground = #191919

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#da4939
palette = 2=#519f50
palette = 3=#ffd24a
palette = 4=#6d9cbe
palette = 5=#d0d0ff
palette = 6=#6e9cbe
palette = 7=#ffffff
palette = 8=#585858
palette = 9=#ff7b6b
palette = 10=#83d182
palette = 11=#ffff7c
palette = 12=#9fcef0
palette = 13=#ffffff
palette = 14=#a0cef0
palette = 15=#ffffff
background = #2b2b2b
foreground = #e6e1dc
cursor-color = #ffffff
cursor-text = #c0bbb6
selection-background = #5a647e
selection-foreground = #e6e1dc

View File

@@ -0,0 +1,22 @@
palette = 0=#1f1f1f
palette = 1=#f81118
palette = 2=#2dc55e
palette = 3=#ecba0f
palette = 4=#2a84d2
palette = 5=#4e5ab7
palette = 6=#1081d6
palette = 7=#d6dbe5
palette = 8=#d6dbe5
palette = 9=#de352e
palette = 10=#1dd361
palette = 11=#f3bd09
palette = 12=#1081d6
palette = 13=#5350b9
palette = 14=#0f7ddb
palette = 15=#ffffff
background = #131313
foreground = #d6dbe5
cursor-color = #b9b9b9
cursor-text = #101010
selection-background = #1f1f1f
selection-foreground = #d6dbe5

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#bb0000
palette = 2=#00bb00
palette = 3=#bbbb00
palette = 4=#0d0dc8
palette = 5=#bb00bb
palette = 6=#00bbbb
palette = 7=#bbbbbb
palette = 8=#555555
palette = 9=#ff5555
palette = 10=#55ff55
palette = 11=#ffff55
palette = 12=#5555ff
palette = 13=#ff55ff
palette = 14=#55ffff
palette = 15=#ffffff
background = #000000
foreground = #bbbbbb
cursor-color = #bbbbbb
cursor-text = #ffffff
selection-background = #b5d5ff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#bb0000
palette = 2=#00bb00
palette = 3=#bbbb00
palette = 4=#0000bb
palette = 5=#bb00bb
palette = 6=#00bbbb
palette = 7=#bbbbbb
palette = 8=#555555
palette = 9=#ff5555
palette = 10=#2fd92f
palette = 11=#bfbf15
palette = 12=#5555ff
palette = 13=#ff55ff
palette = 14=#22cccc
palette = 15=#ffffff
background = #ffffff
foreground = #000000
cursor-color = #000000
cursor-text = #ffffff
selection-background = #b5d5ff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#4f4f4f
palette = 1=#ff6c60
palette = 2=#a8ff60
palette = 3=#ffffb6
palette = 4=#96cbfe
palette = 5=#ff73fd
palette = 6=#c6c5fe
palette = 7=#eeeeee
palette = 8=#7c7c7c
palette = 9=#ffb6b0
palette = 10=#ceffac
palette = 11=#ffffcc
palette = 12=#b5dcff
palette = 13=#ff9cfe
palette = 14=#dfdffe
palette = 15=#ffffff
background = #000000
foreground = #bbbbbb
cursor-color = #ffa560
cursor-text = #ffffff
selection-background = #363983
selection-foreground = #f2f2f2

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#cc0000
palette = 2=#4e9a06
palette = 3=#c4a000
palette = 4=#3465a4
palette = 5=#75507b
palette = 6=#06989a
palette = 7=#d3d7cf
palette = 8=#555753
palette = 9=#ef2929
palette = 10=#8ae234
palette = 11=#fce94f
palette = 12=#729fcf
palette = 13=#ad7fa8
palette = 14=#34e2e2
palette = 15=#eeeeec
background = #000000
foreground = #ffffff
cursor-color = #ffffff
cursor-text = #000000
selection-background = #b5d5ff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#cc0000
palette = 2=#4e9a06
palette = 3=#c4a000
palette = 4=#3465a4
palette = 5=#75507b
palette = 6=#06989a
palette = 7=#b9bdb5
palette = 8=#555753
palette = 9=#ef2929
palette = 10=#7dd527
palette = 11=#d6c329
palette = 12=#729fcf
palette = 13=#ad7fa8
palette = 14=#27d5d5
palette = 15=#eeeeec
background = #ffffff
foreground = #000000
cursor-color = #000000
cursor-text = #ffffff
selection-background = #b5d5ff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#090300
palette = 1=#a2524c
palette = 2=#55a049
palette = 3=#bfce72
palette = 4=#6657b3
palette = 5=#984ca3
palette = 6=#67b6bd
palette = 7=#ffffff
palette = 8=#000000
palette = 9=#a2524c
palette = 10=#55a049
palette = 11=#bfce72
palette = 12=#6657b3
palette = 13=#984ca3
palette = 14=#67b6bd
palette = 15=#f7f7f7
background = #40318d
foreground = #7869c4
cursor-color = #7869c4
cursor-text = #40318d
selection-background = #7869c4
selection-foreground = #40318d

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#aa0000
palette = 2=#00aa00
palette = 3=#aa5500
palette = 4=#0d0db7
palette = 5=#aa00aa
palette = 6=#00aaaa
palette = 7=#aaaaaa
palette = 8=#555555
palette = 9=#ff5555
palette = 10=#55ff55
palette = 11=#ffff55
palette = 12=#5555ff
palette = 13=#ff55ff
palette = 14=#55ffff
palette = 15=#ffffff
background = #000000
foreground = #aaaaaa
cursor-color = #b8b8b8
cursor-text = #ffffff
selection-background = #c1deff
selection-foreground = #000000

View File

@@ -0,0 +1,22 @@
palette = 0=#000000
palette = 1=#f8282a
palette = 2=#328a5d
palette = 3=#fa701d
palette = 4=#135cd0
palette = 5=#9f00bd
palette = 6=#33c3c1
palette = 7=#b3b3b3
palette = 8=#555753
palette = 9=#fb0416
palette = 10=#2cc631
palette = 11=#e3bd0e
palette = 12=#1670ff
palette = 13=#e900b0
palette = 14=#3ad5ce
palette = 15=#eeeeec
background = #ffffff
foreground = #262626
cursor-color = #62c6ef
cursor-text = #ffffff
selection-background = #6fd3fc
selection-foreground = #041730

View File

@@ -0,0 +1,22 @@
palette = 0=#2f2833
palette = 1=#fc644d
palette = 2=#a5f69c
palette = 3=#e9d7a5
palette = 4=#3b79c7
palette = 5=#f92672
palette = 6=#74d3de
palette = 7=#d5ced9
palette = 8=#7e6c88
palette = 9=#fc644d
palette = 10=#a5f69c
palette = 11=#e9d7a5
palette = 12=#3b79c7
palette = 13=#f92672
palette = 14=#74d3de
palette = 15=#ffffff
background = #2f2833
foreground = #d5ced9
cursor-color = #d5ced9
cursor-text = #2f2833
selection-background = #7e6c88
selection-foreground = #d5ced9

View File

@@ -0,0 +1,22 @@
palette = 0=#282828
palette = 1=#ee5396
palette = 2=#25be6a
palette = 3=#08bdba
palette = 4=#78a9ff
palette = 5=#be95ff
palette = 6=#33b1ff
palette = 7=#dfdfe0
palette = 8=#484848
palette = 9=#f16da6
palette = 10=#46c880
palette = 11=#2dc7c4
palette = 12=#8cb6ff
palette = 13=#c8a5ff
palette = 14=#52bdff
palette = 15=#e4e4e5
background = #161616
foreground = #f2f4f8
cursor-color = #f2f4f8
cursor-text = #161616
selection-background = #2a2a2a
selection-foreground = #f2f4f8

View File

@@ -0,0 +1,22 @@
palette = 0=#51576d
palette = 1=#e78284
palette = 2=#a6d189
palette = 3=#e5c890
palette = 4=#8caaee
palette = 5=#f4b8e4
palette = 6=#81c8be
palette = 7=#a5adce
palette = 8=#626880
palette = 9=#e67172
palette = 10=#8ec772
palette = 11=#d9ba73
palette = 12=#7b9ef0
palette = 13=#f2a4db
palette = 14=#5abfb5
palette = 15=#b5bfe2
background = #303446
foreground = #c6d0f5
cursor-color = #f2d5cf
cursor-text = #303446
selection-background = #626880
selection-foreground = #c6d0f5

View File

@@ -0,0 +1,22 @@
palette = 0=#5c5f77
palette = 1=#d20f39
palette = 2=#40a02b
palette = 3=#df8e1d
palette = 4=#1e66f5
palette = 5=#ea76cb
palette = 6=#179299
palette = 7=#acb0be
palette = 8=#6c6f85
palette = 9=#de293e
palette = 10=#49af3d
palette = 11=#eea02d
palette = 12=#456eff
palette = 13=#fe85d8
palette = 14=#2d9fa8
palette = 15=#bcc0cc
background = #eff1f5
foreground = #4c4f69
cursor-color = #dc8a78
cursor-text = #eff1f5
selection-background = #acb0be
selection-foreground = #4c4f69

View File

@@ -0,0 +1,22 @@
palette = 0=#494d64
palette = 1=#ed8796
palette = 2=#a6da95
palette = 3=#eed49f
palette = 4=#8aadf4
palette = 5=#f5bde6
palette = 6=#8bd5ca
palette = 7=#a5adcb
palette = 8=#5b6078
palette = 9=#ec7486
palette = 10=#8ccf7f
palette = 11=#e1c682
palette = 12=#78a1f6
palette = 13=#f2a9dd
palette = 14=#63cbc0
palette = 15=#b8c0e0
background = #24273a
foreground = #cad3f5
cursor-color = #f4dbd6
cursor-text = #24273a
selection-background = #5b6078
selection-foreground = #cad3f5

View File

@@ -0,0 +1,22 @@
palette = 0=#45475a
palette = 1=#f38ba8
palette = 2=#a6e3a1
palette = 3=#f9e2af
palette = 4=#89b4fa
palette = 5=#f5c2e7
palette = 6=#94e2d5
palette = 7=#a6adc8
palette = 8=#585b70
palette = 9=#f37799
palette = 10=#89d88b
palette = 11=#ebd391
palette = 12=#74a8fc
palette = 13=#f2aede
palette = 14=#6bd7ca
palette = 15=#bac2de
background = #1e1e2e
foreground = #cdd6f4
cursor-color = #f5e0dc
cursor-text = #1e1e2e
selection-background = #585b70
selection-foreground = #cdd6f4

Some files were not shown because too many files have changed in this diff Show More