Rename to cmux and add About panel
This commit is contained in:
parent
f6034a5979
commit
8320d5805a
18 changed files with 164 additions and 125 deletions
32
CLAUDE.md
32
CLAUDE.md
|
|
@ -1,4 +1,4 @@
|
|||
# GhosttyTabs
|
||||
# cmux
|
||||
|
||||
A macOS terminal app with vertical tabs, using libghostty (GhosttyKit.xcframework) for terminal emulation.
|
||||
|
||||
|
|
@ -12,9 +12,9 @@ A macOS terminal app with vertical tabs, using libghostty (GhosttyKit.xcframewor
|
|||
### Build and launch (Release)
|
||||
```bash
|
||||
cd /Users/lawrencechen/fun/cmux-terminal/GhosttyTabs
|
||||
pkill -9 GhosttyTabs 2>/dev/null
|
||||
xcodebuild -scheme GhosttyTabs -configuration Release build
|
||||
open ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-cbjivvtpirygxbbgqlpdpiiyjnwh/Build/Products/Release/GhosttyTabs.app
|
||||
pkill -9 cmux 2>/dev/null
|
||||
xcodebuild -scheme cmux -configuration Release build
|
||||
open ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-cbjivvtpirygxbbgqlpdpiiyjnwh/Build/Products/Release/cmux.app
|
||||
```
|
||||
|
||||
### Rebuild libghostty (optimized)
|
||||
|
|
@ -26,18 +26,18 @@ cp -R /tmp/ghostty/macos/GhosttyKit.xcframework /Users/lawrencechen/fun/cmux-ter
|
|||
|
||||
### Project structure
|
||||
- `Sources/` - Swift source files
|
||||
- `GhosttyTabsApp.swift` - App entry point with keyboard shortcuts
|
||||
- `cmuxApp.swift` - App entry point with keyboard shortcuts
|
||||
- `ContentView.swift` - Main UI with vertical tabs sidebar
|
||||
- `TabManager.swift` - Tab state management
|
||||
- `GhosttyTerminalView.swift` - libghostty terminal integration
|
||||
- `GhosttyConfig.swift` - Ghostty config parser
|
||||
- `TerminalController.swift` - Unix socket server for programmatic control
|
||||
- `tests/` - Test files and utilities
|
||||
- `ghosttytabs.py` - Python client library for socket API
|
||||
- `cmux.py` - Python client library for socket API
|
||||
- `test_ctrl_socket.py` - Main automated test suite
|
||||
- `GhosttyKit.xcframework/` - libghostty static library (gitignored, rebuild from /tmp/ghostty)
|
||||
- `ghostty.h` - Ghostty C API header
|
||||
- `GhosttyTabs-Bridging-Header.h` - Swift bridging header
|
||||
- `cmux-Bridging-Header.h` - Swift bridging header
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- `Cmd+T` / `Cmd+N` / `Ctrl+Shift+`` - New tab
|
||||
|
|
@ -54,7 +54,7 @@ Reads user's Ghostty config from:
|
|||
|
||||
### Unix Socket Control API
|
||||
|
||||
GhosttyTabs exposes a Unix socket at `/tmp/ghosttytabs.sock` for programmatic control and automated testing. The socket is created when the app launches.
|
||||
cmux exposes a Unix socket at `/tmp/cmux.sock` for programmatic control and automated testing. The socket is created when the app launches.
|
||||
|
||||
#### Socket Commands
|
||||
|
||||
|
|
@ -84,12 +84,12 @@ Use `\n` for Enter (carriage return), `\t` for tab, `\r` for raw CR.
|
|||
|
||||
### Python Client Library
|
||||
|
||||
Located at `tests/ghosttytabs.py`:
|
||||
Located at `tests/cmux.py`:
|
||||
|
||||
```python
|
||||
from ghosttytabs import GhosttyTabs
|
||||
from cmux import cmux
|
||||
|
||||
with GhosttyTabs() as client:
|
||||
with cmux() as client:
|
||||
# Send text with Enter
|
||||
client.send("echo hello\n")
|
||||
|
||||
|
|
@ -108,16 +108,16 @@ with GhosttyTabs() as client:
|
|||
|
||||
```bash
|
||||
# Build and launch the app first
|
||||
pkill -9 GhosttyTabs 2>/dev/null
|
||||
xcodebuild -scheme GhosttyTabs -configuration Release build
|
||||
open ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-cbjivvtpirygxbbgqlpdpiiyjnwh/Build/Products/Release/GhosttyTabs.app
|
||||
pkill -9 cmux 2>/dev/null
|
||||
xcodebuild -scheme cmux -configuration Release build
|
||||
open ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-cbjivvtpirygxbbgqlpdpiiyjnwh/Build/Products/Release/cmux.app
|
||||
sleep 3
|
||||
|
||||
# Run the main test suite (tests Ctrl+C, Ctrl+D)
|
||||
python3 tests/test_ctrl_socket.py
|
||||
|
||||
# Interactive CLI for manual testing
|
||||
python3 tests/ghosttytabs.py
|
||||
python3 tests/cmux.py
|
||||
```
|
||||
|
||||
### Writing New Tests
|
||||
|
|
@ -148,7 +148,7 @@ python3 tests/ghosttytabs.py
|
|||
|
||||
### Test Files
|
||||
|
||||
- `tests/ghosttytabs.py` - Python client library for socket API
|
||||
- `tests/cmux.py` - Python client library for socket API
|
||||
- `tests/test_ctrl_socket.py` - Automated Ctrl+C/D test suite (main tests)
|
||||
- `tests/test_signals_auto.py` - PTY-based signal tests (standalone)
|
||||
- `tests/test_ctrl_interactive.py` - Interactive manual tests
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A5001001 /* GhosttyTabsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001011 /* GhosttyTabsApp.swift */; };
|
||||
A5001001 /* cmuxApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001011 /* cmuxApp.swift */; };
|
||||
A5001002 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001012 /* ContentView.swift */; };
|
||||
A5001003 /* TabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001013 /* TabManager.swift */; };
|
||||
A5001004 /* GhosttyConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001014 /* GhosttyConfig.swift */; };
|
||||
|
|
@ -49,16 +49,16 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
A5001000 /* GhosttyTabs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GhosttyTabs.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5001000 /* cmux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmux.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7E7E6EF344A568AC7FEE3715 /* GhosttyTabsUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GhosttyTabsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5001011 /* GhosttyTabsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyTabsApp.swift; sourceTree = "<group>"; };
|
||||
A5001011 /* cmuxApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmuxApp.swift; sourceTree = "<group>"; };
|
||||
A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
A5001013 /* TabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabManager.swift; sourceTree = "<group>"; };
|
||||
A5001014 /* GhosttyConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyConfig.swift; sourceTree = "<group>"; };
|
||||
A5001015 /* GhosttyTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyTerminalView.swift; sourceTree = "<group>"; };
|
||||
A5001016 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
|
||||
A5001017 /* ghostty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ghostty.h; sourceTree = "<group>"; };
|
||||
A5001018 /* GhosttyTabs-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GhosttyTabs-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
A5001018 /* cmux-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "cmux-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
A5001019 /* TerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalController.swift; sourceTree = "<group>"; };
|
||||
A5001090 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
A5001091 /* NotificationsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPage.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
A5001101 /* Assets.xcassets */,
|
||||
A5001016 /* GhosttyKit.xcframework */,
|
||||
A5001017 /* ghostty.h */,
|
||||
A5001018 /* GhosttyTabs-Bridging-Header.h */,
|
||||
A5001018 /* cmux-Bridging-Header.h */,
|
||||
3196C9C2D01F054C1D3385DD /* GhosttyTabsUITests */,
|
||||
A5001042 /* Products */,
|
||||
);
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
A5001041 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5001011 /* GhosttyTabsApp.swift */,
|
||||
A5001011 /* cmuxApp.swift */,
|
||||
A5001012 /* ContentView.swift */,
|
||||
A5001013 /* TabManager.swift */,
|
||||
A5001014 /* GhosttyConfig.swift */,
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
A5001042 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5001000 /* GhosttyTabs.app */,
|
||||
A5001000 /* cmux.app */,
|
||||
7E7E6EF344A568AC7FEE3715 /* GhosttyTabsUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
);
|
||||
name = GhosttyTabs;
|
||||
productName = GhosttyTabs;
|
||||
productReference = A5001000 /* GhosttyTabs.app */;
|
||||
productReference = A5001000 /* cmux.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
CB450DF0F0B3839599082C4D /* GhosttyTabsUITests */ = {
|
||||
|
|
@ -240,7 +240,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A5001001 /* GhosttyTabsApp.swift in Sources */,
|
||||
A5001001 /* cmuxApp.swift in Sources */,
|
||||
A5001002 /* ContentView.swift in Sources */,
|
||||
A5001003 /* TabManager.swift in Sources */,
|
||||
A5001004 /* GhosttyConfig.swift in Sources */,
|
||||
|
|
@ -346,6 +346,8 @@
|
|||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = cmux;
|
||||
INFOPLIST_KEY_CFBundleName = cmux;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = "";
|
||||
|
|
@ -368,10 +370,10 @@
|
|||
"-framework",
|
||||
Carbon,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ghosttytabs.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.app;
|
||||
PRODUCT_NAME = cmux;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "GhosttyTabs-Bridging-Header.h";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
|
|
@ -387,6 +389,8 @@
|
|||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = cmux;
|
||||
INFOPLIST_KEY_CFBundleName = cmux;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = "";
|
||||
|
|
@ -410,10 +414,10 @@
|
|||
Carbon,
|
||||
);
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ghosttytabs.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.app;
|
||||
PRODUCT_NAME = cmux;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "GhosttyTabs-Bridging-Header.h";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
|
@ -427,7 +431,7 @@
|
|||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ghosttytabs.appuitests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -444,7 +448,7 @@
|
|||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ghosttytabs.appuitests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_TARGET_NAME = GhosttyTabs;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="GhosttyTabs.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
|
|
@ -14,17 +14,17 @@
|
|||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="GhosttyTabs.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction buildConfiguration="Release" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" allowLocationSimulation="YES">
|
||||
<BuildableProductRunnable runnableDebuggingMode="0">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="GhosttyTabs.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" savedToolIdentifier="" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES">
|
||||
<BuildableProductRunnable runnableDebuggingMode="0">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="GhosttyTabs.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction buildConfiguration="Release"/>
|
||||
|
|
@ -2,19 +2,19 @@
|
|||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "GhosttyTabs",
|
||||
name: "cmux",
|
||||
platforms: [
|
||||
.macOS(.v13)
|
||||
],
|
||||
products: [
|
||||
.executable(name: "GhosttyTabs", targets: ["GhosttyTabs"])
|
||||
.executable(name: "cmux", targets: ["cmux"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/migueldeicaza/SwiftTerm.git", from: "1.2.0")
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "GhosttyTabs",
|
||||
name: "cmux",
|
||||
dependencies: ["SwiftTerm"],
|
||||
path: "Sources"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# GhosttyTabs
|
||||
# cmux
|
||||
|
||||
Vertical tabs for Ghostty on macOS, built on libghostty.
|
||||
|
||||
[](releases/latest/download/GhosttyTabs-macos.zip)
|
||||
[](releases/latest/download/cmux-macos.zip)
|
||||
|
||||
## Releases
|
||||
|
||||
Tag a version like `v0.1.0` and push it to trigger the GitHub Actions release workflow.
|
||||
The workflow builds `GhosttyKit.xcframework`, builds the Release app, signs, notarizes,
|
||||
staples, and uploads `GhosttyTabs-macos.zip` to the release.
|
||||
staples, and uploads `cmux-macos.zip` to the release.
|
||||
|
||||
### Required GitHub secrets
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
registerLaunchServicesBundle()
|
||||
enforceSingleInstance()
|
||||
ensureApplicationIcon()
|
||||
observeDuplicateLaunches()
|
||||
configureUserNotifications()
|
||||
}
|
||||
|
|
@ -46,6 +47,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
center.delegate = self
|
||||
}
|
||||
|
||||
private func ensureApplicationIcon() {
|
||||
if let icon = NSImage(named: NSImage.applicationIconName) {
|
||||
NSApplication.shared.applicationIconImage = icon
|
||||
}
|
||||
}
|
||||
|
||||
private func registerLaunchServicesBundle() {
|
||||
let bundleURL = Bundle.main.bundleURL.standardizedFileURL
|
||||
let registerStatus = LSRegisterURL(bundleURL as CFURL, true)
|
||||
|
|
|
|||
|
|
@ -70,12 +70,18 @@ class GhosttyApp {
|
|||
private(set) var defaultBackgroundColor: NSColor = .windowBackgroundColor
|
||||
private(set) var defaultBackgroundOpacity: Double = 1.0
|
||||
let backgroundLogEnabled = {
|
||||
if ProcessInfo.processInfo.environment["CMUX_DEBUG_BG"] == "1" {
|
||||
return true
|
||||
}
|
||||
if ProcessInfo.processInfo.environment["GHOSTTYTABS_DEBUG_BG"] == "1" {
|
||||
return true
|
||||
}
|
||||
if UserDefaults.standard.bool(forKey: "cmuxDebugBG") {
|
||||
return true
|
||||
}
|
||||
return UserDefaults.standard.bool(forKey: "GhosttyTabsDebugBG")
|
||||
}()
|
||||
private let backgroundLogURL = URL(fileURLWithPath: "/tmp/ghosttytabs-bg.log")
|
||||
private let backgroundLogURL = URL(fileURLWithPath: "/tmp/cmux-bg.log")
|
||||
private var appObservers: [NSObjectProtocol] = []
|
||||
private var displayLink: CVDisplayLink?
|
||||
private var displayLinkUsers = 0
|
||||
|
|
@ -555,7 +561,7 @@ class GhosttyApp {
|
|||
}
|
||||
|
||||
func logBackground(_ message: String) {
|
||||
let line = "GhosttyTabs bg: \(message)\n"
|
||||
let line = "cmux bg: \(message)\n"
|
||||
if let data = line.data(using: .utf8) {
|
||||
if FileManager.default.fileExists(atPath: backgroundLogURL.path) == false {
|
||||
FileManager.default.createFile(atPath: backgroundLogURL.path, contents: nil)
|
||||
|
|
@ -1696,7 +1702,7 @@ final class GhosttySurfaceScrollView: NSView {
|
|||
CAMediaTimingFunction(name: .easeOut),
|
||||
CAMediaTimingFunction(name: .easeIn)
|
||||
]
|
||||
self.flashLayer.add(animation, forKey: "ghosttytabs.flash")
|
||||
self.flashLayer.add(animation, forKey: "cmux.flash")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Foundation
|
|||
class TerminalController {
|
||||
static let shared = TerminalController()
|
||||
|
||||
private let socketPath = "/tmp/ghosttytabs.sock"
|
||||
private let socketPath = "/tmp/cmux.sock"
|
||||
private var serverSocket: Int32 = -1
|
||||
private var isRunning = false
|
||||
private var clientHandlers: [Int32: Thread] = [:]
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ struct TerminalNotification: Identifiable, Hashable {
|
|||
final class TerminalNotificationStore: ObservableObject {
|
||||
static let shared = TerminalNotificationStore()
|
||||
|
||||
static let categoryIdentifier = "com.cmux.ghosttytabs.userNotification"
|
||||
static let actionShowIdentifier = "com.cmux.ghosttytabs.userNotification.show"
|
||||
static let categoryIdentifier = "com.cmux.app.userNotification"
|
||||
static let actionShowIdentifier = "com.cmux.app.userNotification.show"
|
||||
|
||||
@Published private(set) var notifications: [TerminalNotification] = []
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct GhosttyTabsApp: App {
|
||||
struct cmuxApp: App {
|
||||
@StateObject private var tabManager = TabManager()
|
||||
@StateObject private var notificationStore = TerminalNotificationStore.shared
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
|
@ -24,6 +25,12 @@ struct GhosttyTabsApp: App {
|
|||
}
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
.commands {
|
||||
CommandGroup(replacing: .appInfo) {
|
||||
Button("About cmux") {
|
||||
showAboutPanel()
|
||||
}
|
||||
}
|
||||
|
||||
// New tab commands
|
||||
CommandGroup(replacing: .newItem) {
|
||||
Button("New Tab") {
|
||||
|
|
@ -93,4 +100,19 @@ struct GhosttyTabsApp: App {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func showAboutPanel() {
|
||||
let bundle = Bundle.main
|
||||
let appName = bundle.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
?? bundle.object(forInfoDictionaryKey: "CFBundleName") as? String
|
||||
?? "cmux"
|
||||
let version = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0"
|
||||
let build = bundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1"
|
||||
NSApp.orderFrontStandardAboutPanel(options: [
|
||||
.applicationName: appName,
|
||||
.version: version,
|
||||
.applicationVersion: build
|
||||
])
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
#!/bin/bash
|
||||
# Rebuild and restart GhosttyTabs app
|
||||
# Rebuild and restart cmux app
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Kill existing app if running
|
||||
pkill -9 -f "GhosttyTabs" 2>/dev/null || true
|
||||
pkill -9 -f "cmux" 2>/dev/null || true
|
||||
|
||||
# Build
|
||||
swift build
|
||||
|
||||
# Copy to app bundle
|
||||
cp .build/debug/GhosttyTabs .build/debug/GhosttyTabs.app/Contents/MacOS/
|
||||
cp .build/debug/cmux .build/debug/cmux.app/Contents/MacOS/
|
||||
|
||||
# Open the app
|
||||
open .build/debug/GhosttyTabs.app
|
||||
open .build/debug/cmux.app
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
GhosttyTabs Python Client
|
||||
cmux Python Client
|
||||
|
||||
A client library for programmatically controlling GhosttyTabs via Unix socket.
|
||||
A client library for programmatically controlling cmux via Unix socket.
|
||||
|
||||
Usage:
|
||||
from ghosttytabs import GhosttyTabs
|
||||
from cmux import cmux
|
||||
|
||||
client = GhosttyTabs()
|
||||
client = cmux()
|
||||
client.connect()
|
||||
|
||||
# Send text to terminal
|
||||
|
|
@ -33,29 +33,29 @@ import os
|
|||
from typing import Optional, List, Tuple
|
||||
|
||||
|
||||
class GhosttyTabsError(Exception):
|
||||
"""Exception raised for GhosttyTabs errors"""
|
||||
class cmuxError(Exception):
|
||||
"""Exception raised for cmux errors"""
|
||||
pass
|
||||
|
||||
|
||||
class GhosttyTabs:
|
||||
"""Client for controlling GhosttyTabs via Unix socket"""
|
||||
class cmux:
|
||||
"""Client for controlling cmux via Unix socket"""
|
||||
|
||||
DEFAULT_SOCKET_PATH = "/tmp/ghosttytabs.sock"
|
||||
DEFAULT_SOCKET_PATH = "/tmp/cmux.sock"
|
||||
|
||||
def __init__(self, socket_path: str = None):
|
||||
self.socket_path = socket_path or self.DEFAULT_SOCKET_PATH
|
||||
self._socket: Optional[socket.socket] = None
|
||||
|
||||
def connect(self) -> None:
|
||||
"""Connect to the GhosttyTabs socket"""
|
||||
"""Connect to the cmux socket"""
|
||||
if self._socket is not None:
|
||||
return
|
||||
|
||||
if not os.path.exists(self.socket_path):
|
||||
raise GhosttyTabsError(
|
||||
raise cmuxError(
|
||||
f"Socket not found at {self.socket_path}. "
|
||||
"Is GhosttyTabs running?"
|
||||
"Is cmux running?"
|
||||
)
|
||||
|
||||
self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
|
|
@ -64,7 +64,7 @@ class GhosttyTabs:
|
|||
self._socket.settimeout(5.0)
|
||||
except socket.error as e:
|
||||
self._socket = None
|
||||
raise GhosttyTabsError(f"Failed to connect: {e}")
|
||||
raise cmuxError(f"Failed to connect: {e}")
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the connection"""
|
||||
|
|
@ -83,16 +83,16 @@ class GhosttyTabs:
|
|||
def _send_command(self, command: str) -> str:
|
||||
"""Send a command and receive response"""
|
||||
if self._socket is None:
|
||||
raise GhosttyTabsError("Not connected")
|
||||
raise cmuxError("Not connected")
|
||||
|
||||
try:
|
||||
self._socket.sendall((command + "\n").encode())
|
||||
response = self._socket.recv(8192).decode().strip()
|
||||
return response
|
||||
except socket.timeout:
|
||||
raise GhosttyTabsError("Command timed out")
|
||||
raise cmuxError("Command timed out")
|
||||
except socket.error as e:
|
||||
raise GhosttyTabsError(f"Socket error: {e}")
|
||||
raise cmuxError(f"Socket error: {e}")
|
||||
|
||||
def ping(self) -> bool:
|
||||
"""Check if the server is responding"""
|
||||
|
|
@ -126,25 +126,25 @@ class GhosttyTabs:
|
|||
response = self._send_command("new_tab")
|
||||
if response.startswith("OK "):
|
||||
return response[3:]
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def new_split(self, direction: str) -> None:
|
||||
"""Create a split in the given direction (left/right/up/down)."""
|
||||
response = self._send_command(f"new_split {direction}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def close_tab(self, tab_id: str) -> None:
|
||||
"""Close a tab by ID"""
|
||||
response = self._send_command(f"close_tab {tab_id}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def select_tab(self, tab: str | int) -> None:
|
||||
"""Select a tab by ID or index"""
|
||||
response = self._send_command(f"select_tab {tab}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def list_surfaces(self, tab: str | int | None = None) -> List[Tuple[int, str, bool]]:
|
||||
"""
|
||||
|
|
@ -172,13 +172,13 @@ class GhosttyTabs:
|
|||
"""Focus a surface by ID or index in the current tab."""
|
||||
response = self._send_command(f"focus_surface {surface}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def current_tab(self) -> str:
|
||||
"""Get the current tab's ID"""
|
||||
response = self._send_command("current_tab")
|
||||
if response.startswith("ERROR"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
return response
|
||||
|
||||
def send(self, text: str) -> None:
|
||||
|
|
@ -195,14 +195,14 @@ class GhosttyTabs:
|
|||
escaped = text.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
|
||||
response = self._send_command(f"send {escaped}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def send_surface(self, surface: str | int, text: str) -> None:
|
||||
"""Send text to a specific surface by ID or index in the current tab."""
|
||||
escaped = text.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
|
||||
response = self._send_command(f"send_surface {surface} {escaped}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def send_key(self, key: str) -> None:
|
||||
"""
|
||||
|
|
@ -215,13 +215,13 @@ class GhosttyTabs:
|
|||
"""
|
||||
response = self._send_command(f"send_key {key}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def send_key_surface(self, surface: str | int, key: str) -> None:
|
||||
"""Send a special key to a specific surface by ID or index in the current tab."""
|
||||
response = self._send_command(f"send_key_surface {surface} {key}")
|
||||
if not response.startswith("OK"):
|
||||
raise GhosttyTabsError(response)
|
||||
raise cmuxError(response)
|
||||
|
||||
def send_line(self, text: str) -> None:
|
||||
"""Send text followed by Enter"""
|
||||
|
|
@ -241,23 +241,23 @@ class GhosttyTabs:
|
|||
|
||||
|
||||
def main():
|
||||
"""CLI interface for ghosttytabs"""
|
||||
"""CLI interface for cmux"""
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="GhosttyTabs CLI")
|
||||
parser = argparse.ArgumentParser(description="cmux CLI")
|
||||
parser.add_argument("command", nargs="?", help="Command to send")
|
||||
parser.add_argument("args", nargs="*", help="Command arguments")
|
||||
parser.add_argument("-s", "--socket", default=GhosttyTabs.DEFAULT_SOCKET_PATH,
|
||||
parser.add_argument("-s", "--socket", default=cmux.DEFAULT_SOCKET_PATH,
|
||||
help="Socket path")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
with GhosttyTabs(args.socket) as client:
|
||||
with cmux(args.socket) as client:
|
||||
if not args.command:
|
||||
# Interactive mode
|
||||
print("GhosttyTabs CLI (type 'help' for commands, 'quit' to exit)")
|
||||
print("cmux CLI (type 'help' for commands, 'quit' to exit)")
|
||||
while True:
|
||||
try:
|
||||
line = input("> ").strip()
|
||||
|
|
@ -278,7 +278,7 @@ def main():
|
|||
command += " " + " ".join(args.args)
|
||||
response = client._send_command(command)
|
||||
print(response)
|
||||
except GhosttyTabsError as e:
|
||||
except cmuxError as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
|
@ -1,24 +1,24 @@
|
|||
#!/bin/bash
|
||||
# Test script that sends keystrokes to GhosttyTabs via AppleScript
|
||||
# Test script that sends keystrokes to cmux via AppleScript
|
||||
# This tests the actual keyboard input path through the app
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== GhosttyTabs Keystroke Test ==="
|
||||
echo "=== cmux Keystroke Test ==="
|
||||
echo ""
|
||||
|
||||
# Check if GhosttyTabs is running
|
||||
if ! pgrep -x "GhosttyTabs" > /dev/null; then
|
||||
echo "Error: GhosttyTabs is not running"
|
||||
echo "Please start GhosttyTabs first"
|
||||
# Check if cmux is running
|
||||
if ! pgrep -x "cmux" > /dev/null; then
|
||||
echo "Error: cmux is not running"
|
||||
echo "Please start cmux first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "GhosttyTabs is running"
|
||||
echo "cmux is running"
|
||||
echo ""
|
||||
|
||||
# Activate GhosttyTabs
|
||||
osascript -e 'tell application "GhosttyTabs" to activate'
|
||||
# Activate cmux
|
||||
osascript -e 'tell application "cmux" to activate'
|
||||
sleep 0.5
|
||||
|
||||
echo "Test 1: Testing Ctrl+C (SIGINT)"
|
||||
|
|
@ -56,7 +56,7 @@ echo " If cat exited, Ctrl+D is working!"
|
|||
echo ""
|
||||
|
||||
echo "=== Manual Verification Required ==="
|
||||
echo "Please check the GhosttyTabs window to verify:"
|
||||
echo "Please check the cmux window to verify:"
|
||||
echo " 1. The 'sleep 30' command was interrupted by Ctrl+C"
|
||||
echo " 2. The 'cat' command exited after Ctrl+D"
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Automated test for ctrl+enter keybind using real keystrokes.
|
||||
|
||||
Requires:
|
||||
- GhosttyTabs running
|
||||
- cmux running
|
||||
- Accessibility permissions for System Events (osascript)
|
||||
- keybind = ctrl+enter=text:\\r (or \\n/\\x0d) configured in Ghostty config
|
||||
"""
|
||||
|
|
@ -14,10 +14,10 @@ import time
|
|||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# Add the directory containing ghosttytabs.py to the path
|
||||
# Add the directory containing cmux.py to the path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from ghosttytabs import GhosttyTabs, GhosttyTabsError
|
||||
from cmux import cmux, cmuxError
|
||||
|
||||
|
||||
def run_osascript(script: str) -> None:
|
||||
|
|
@ -54,7 +54,7 @@ def find_config_with_keybind() -> Path | None:
|
|||
return None
|
||||
|
||||
|
||||
def test_ctrl_enter_keybind(client: GhosttyTabs) -> tuple[bool, str]:
|
||||
def test_ctrl_enter_keybind(client: cmux) -> tuple[bool, str]:
|
||||
marker = Path("/tmp") / f"ghostty_ctrl_enter_{os.getpid()}"
|
||||
marker.unlink(missing_ok=True)
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ def test_ctrl_enter_keybind(client: GhosttyTabs) -> tuple[bool, str]:
|
|||
time.sleep(0.3)
|
||||
|
||||
# Make sure the app is focused for keystrokes
|
||||
run_osascript('tell application "GhosttyTabs" to activate')
|
||||
run_osascript('tell application "cmux" to activate')
|
||||
time.sleep(0.2)
|
||||
|
||||
# Clear any running command
|
||||
|
|
@ -94,14 +94,14 @@ def test_ctrl_enter_keybind(client: GhosttyTabs) -> tuple[bool, str]:
|
|||
|
||||
def run_tests() -> int:
|
||||
print("=" * 60)
|
||||
print("GhosttyTabs Ctrl+Enter Keybind Test")
|
||||
print("cmux Ctrl+Enter Keybind Test")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
socket_path = GhosttyTabs.DEFAULT_SOCKET_PATH
|
||||
socket_path = cmux.DEFAULT_SOCKET_PATH
|
||||
if not os.path.exists(socket_path):
|
||||
print(f"Error: Socket not found at {socket_path}")
|
||||
print("Please make sure GhosttyTabs is running.")
|
||||
print("Please make sure cmux is running.")
|
||||
return 1
|
||||
|
||||
config_path = find_config_with_keybind()
|
||||
|
|
@ -109,19 +109,19 @@ def run_tests() -> int:
|
|||
print("Error: Required keybind not found in Ghostty config.")
|
||||
print("Add a line like:")
|
||||
print(" keybind = ctrl+enter=text:\\r")
|
||||
print("Then restart GhosttyTabs and re-run this test.")
|
||||
print("Then restart cmux and re-run this test.")
|
||||
return 1
|
||||
|
||||
print(f"Using keybind from: {config_path}")
|
||||
print()
|
||||
|
||||
try:
|
||||
with GhosttyTabs() as client:
|
||||
with cmux() as client:
|
||||
ok, message = test_ctrl_enter_keybind(client)
|
||||
status = "✅" if ok else "❌"
|
||||
print(f"{status} {message}")
|
||||
return 0 if ok else 1
|
||||
except GhosttyTabsError as e:
|
||||
except cmuxError as e:
|
||||
print(f"Error: {e}")
|
||||
return 1
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Interactive test for Ctrl+C and Ctrl+D in GhosttyTabs terminal.
|
||||
Interactive test for Ctrl+C and Ctrl+D in cmux terminal.
|
||||
|
||||
This script tests that control signals are properly handled.
|
||||
Run this script inside the GhosttyTabs terminal.
|
||||
Run this script inside the cmux terminal.
|
||||
|
||||
Tests:
|
||||
1. Ctrl+C (SIGINT) - Should interrupt a running process
|
||||
|
|
@ -72,10 +72,10 @@ def test_ctrl_d():
|
|||
|
||||
def main():
|
||||
print("=" * 50)
|
||||
print("GhosttyTabs Control Signal Test")
|
||||
print("cmux Control Signal Test")
|
||||
print("=" * 50)
|
||||
print("\nThis script tests if Ctrl+C and Ctrl+D work correctly.")
|
||||
print("Run this inside the GhosttyTabs terminal to verify the fix.\n")
|
||||
print("Run this inside the cmux terminal to verify the fix.\n")
|
||||
|
||||
# Check if running in a terminal
|
||||
if not os.isatty(sys.stdin.fileno()):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Test script to verify Ctrl+C and Ctrl+D work correctly in the terminal
|
||||
# Run this script inside the GhosttyTabs terminal to test signal handling
|
||||
# Run this script inside the cmux terminal to test signal handling
|
||||
|
||||
set -e
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Automated tests for Ctrl+C and Ctrl+D using the GhosttyTabs socket interface.
|
||||
Automated tests for Ctrl+C and Ctrl+D using the cmux socket interface.
|
||||
|
||||
Usage:
|
||||
python3 test_ctrl_socket.py
|
||||
|
||||
Requirements:
|
||||
- GhosttyTabs must be running with the socket controller enabled
|
||||
- cmux must be running with the socket controller enabled
|
||||
"""
|
||||
|
||||
import os
|
||||
|
|
@ -15,10 +15,10 @@ import time
|
|||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
# Add the directory containing ghosttytabs.py to the path
|
||||
# Add the directory containing cmux.py to the path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from ghosttytabs import GhosttyTabs, GhosttyTabsError
|
||||
from cmux import cmux, cmuxError
|
||||
|
||||
|
||||
class TestResult:
|
||||
|
|
@ -36,7 +36,7 @@ class TestResult:
|
|||
self.message = msg
|
||||
|
||||
|
||||
def test_connection(client: GhosttyTabs) -> TestResult:
|
||||
def test_connection(client: cmux) -> TestResult:
|
||||
"""Test that we can connect and ping the server"""
|
||||
result = TestResult("Connection")
|
||||
try:
|
||||
|
|
@ -49,7 +49,7 @@ def test_connection(client: GhosttyTabs) -> TestResult:
|
|||
return result
|
||||
|
||||
|
||||
def test_ctrl_c(client: GhosttyTabs) -> TestResult:
|
||||
def test_ctrl_c(client: cmux) -> TestResult:
|
||||
"""
|
||||
Test Ctrl+C by:
|
||||
1. Starting sleep command
|
||||
|
|
@ -88,7 +88,7 @@ def test_ctrl_c(client: GhosttyTabs) -> TestResult:
|
|||
return result
|
||||
|
||||
|
||||
def test_ctrl_d(client: GhosttyTabs) -> TestResult:
|
||||
def test_ctrl_d(client: cmux) -> TestResult:
|
||||
"""
|
||||
Test Ctrl+D by:
|
||||
1. Running cat command
|
||||
|
|
@ -127,7 +127,7 @@ def test_ctrl_d(client: GhosttyTabs) -> TestResult:
|
|||
return result
|
||||
|
||||
|
||||
def test_ctrl_c_python(client: GhosttyTabs) -> TestResult:
|
||||
def test_ctrl_c_python(client: cmux) -> TestResult:
|
||||
"""
|
||||
Test Ctrl+C with Python process
|
||||
"""
|
||||
|
|
@ -166,20 +166,20 @@ def test_ctrl_c_python(client: GhosttyTabs) -> TestResult:
|
|||
def run_tests():
|
||||
"""Run all tests"""
|
||||
print("=" * 60)
|
||||
print("GhosttyTabs Ctrl+C/D Automated Tests")
|
||||
print("cmux Ctrl+C/D Automated Tests")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
socket_path = GhosttyTabs.DEFAULT_SOCKET_PATH
|
||||
socket_path = cmux.DEFAULT_SOCKET_PATH
|
||||
if not os.path.exists(socket_path):
|
||||
print(f"Error: Socket not found at {socket_path}")
|
||||
print("Please make sure GhosttyTabs is running.")
|
||||
print("Please make sure cmux is running.")
|
||||
return 1
|
||||
|
||||
results = []
|
||||
|
||||
try:
|
||||
with GhosttyTabs() as client:
|
||||
with cmux() as client:
|
||||
# Test connection
|
||||
print("Testing connection...")
|
||||
results.append(test_connection(client))
|
||||
|
|
@ -215,7 +215,7 @@ def run_tests():
|
|||
print(f" {status} {results[-1].message}")
|
||||
print()
|
||||
|
||||
except GhosttyTabsError as e:
|
||||
except cmuxError as e:
|
||||
print(f"Error: {e}")
|
||||
return 1
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue