Include local changes
This commit is contained in:
parent
4b77b1117d
commit
e234123c17
5 changed files with 223 additions and 2 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -14,8 +14,8 @@ xcuserdata/
|
|||
# Swift Package Manager
|
||||
.swiftpm/
|
||||
|
||||
# GhosttyKit binary (rebuild from /tmp/ghostty with zig build)
|
||||
GhosttyKit.xcframework/
|
||||
# GhosttyKit binary (built from ghostty submodule via scripts/setup.sh)
|
||||
GhosttyKit.xcframework
|
||||
GhosttyKit.xcframework.bak-*/
|
||||
|
||||
# Release artifacts
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
# cmuxterm agent notes
|
||||
|
||||
## Initial setup
|
||||
|
||||
Run the setup script to initialize submodules and build GhosttyKit:
|
||||
|
||||
```bash
|
||||
./scripts/setup.sh
|
||||
```
|
||||
|
||||
## Local dev
|
||||
|
||||
After making code changes, always run the reload script to launch the Debug app:
|
||||
|
|
|
|||
98
CONTRIBUTING.md
Normal file
98
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# Contributing to cmuxterm
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- macOS 14+
|
||||
- Xcode 15+
|
||||
- [Zig](https://ziglang.org/) (install via `brew install zig`)
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Clone the repository with submodules:
|
||||
```bash
|
||||
git clone --recursive https://github.com/manaflow-ai/cmuxterm.git
|
||||
cd cmuxterm
|
||||
```
|
||||
|
||||
2. Run the setup script:
|
||||
```bash
|
||||
./scripts/setup.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Initialize git submodules (ghostty, homebrew-cmuxterm)
|
||||
- Build the GhosttyKit.xcframework from source
|
||||
- Create the necessary symlinks
|
||||
|
||||
3. Build and run the debug app:
|
||||
```bash
|
||||
./scripts/reload.sh
|
||||
```
|
||||
|
||||
## Development Scripts
|
||||
|
||||
| Script | Description |
|
||||
|--------|-------------|
|
||||
| `./scripts/setup.sh` | One-time setup (submodules + xcframework) |
|
||||
| `./scripts/reload.sh` | Build and launch Debug app |
|
||||
| `./scripts/reloadp.sh` | Build and launch Release app |
|
||||
| `./scripts/reload2.sh` | Reload both Debug and Release |
|
||||
| `./scripts/rebuild.sh` | Clean rebuild |
|
||||
|
||||
## Rebuilding GhosttyKit
|
||||
|
||||
If you make changes to the ghostty submodule, rebuild the xcframework:
|
||||
|
||||
```bash
|
||||
cd ghostty
|
||||
zig build -Demit-xcframework=true -Doptimize=ReleaseFast
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Basic tests (run on VM)
|
||||
|
||||
```bash
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmuxterm DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmuxterm DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmuxterm.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py'
|
||||
```
|
||||
|
||||
### UI tests (run on VM)
|
||||
|
||||
```bash
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" -only-testing:GhosttyTabsUITests test'
|
||||
```
|
||||
|
||||
## Ghostty Submodule
|
||||
|
||||
The `ghostty` submodule points to [manaflow-ai/ghostty](https://github.com/manaflow-ai/ghostty), a fork of the upstream Ghostty project.
|
||||
|
||||
### Making changes to ghostty
|
||||
|
||||
```bash
|
||||
cd ghostty
|
||||
git checkout -b my-feature
|
||||
# make changes
|
||||
git add .
|
||||
git commit -m "Description of changes"
|
||||
git push manaflow my-feature
|
||||
```
|
||||
|
||||
### Keeping the fork updated
|
||||
|
||||
```bash
|
||||
cd ghostty
|
||||
git fetch origin
|
||||
git checkout main
|
||||
git merge origin/main
|
||||
git push manaflow main
|
||||
```
|
||||
|
||||
Then update the parent repo:
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
git add ghostty
|
||||
git commit -m "Update ghostty submodule"
|
||||
```
|
||||
|
||||
See `docs/ghostty-fork.md` for details on fork changes and conflict notes.
|
||||
|
|
@ -274,6 +274,9 @@ class TabManager: ObservableObject {
|
|||
let previousSurfaceId = focusedSurfaceId(for: previousTabId) {
|
||||
lastFocusedSurfaceByTab[previousTabId] = previousSurfaceId
|
||||
}
|
||||
if !isNavigatingHistory, let selectedTabId {
|
||||
recordTabInHistory(selectedTabId)
|
||||
}
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.focusSelectedTabSurface(previousTabId: previousTabId)
|
||||
self?.updateWindowTitleForSelectedTab()
|
||||
|
|
@ -287,6 +290,12 @@ class TabManager: ObservableObject {
|
|||
private var suppressFocusFlash = false
|
||||
private var lastFocusedSurfaceByTab: [UUID: UUID] = [:]
|
||||
|
||||
// Recent tab history for back/forward navigation (like browser history)
|
||||
private var tabHistory: [UUID] = []
|
||||
private var historyIndex: Int = -1
|
||||
private var isNavigatingHistory = false
|
||||
private let maxHistorySize = 50
|
||||
|
||||
init() {
|
||||
addTab()
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
|
|
@ -687,6 +696,82 @@ class TabManager: ObservableObject {
|
|||
selectedTabId = lastTab.id
|
||||
}
|
||||
|
||||
// MARK: - Recent Tab History Navigation
|
||||
|
||||
private func recordTabInHistory(_ tabId: UUID) {
|
||||
// If we're not at the end of history, truncate forward history
|
||||
if historyIndex < tabHistory.count - 1 {
|
||||
tabHistory = Array(tabHistory.prefix(historyIndex + 1))
|
||||
}
|
||||
|
||||
// Don't add duplicate consecutive entries
|
||||
if tabHistory.last == tabId {
|
||||
return
|
||||
}
|
||||
|
||||
tabHistory.append(tabId)
|
||||
|
||||
// Trim history if it exceeds max size
|
||||
if tabHistory.count > maxHistorySize {
|
||||
tabHistory.removeFirst(tabHistory.count - maxHistorySize)
|
||||
}
|
||||
|
||||
historyIndex = tabHistory.count - 1
|
||||
}
|
||||
|
||||
func navigateBack() {
|
||||
guard historyIndex > 0 else { return }
|
||||
|
||||
// Find the previous valid tab in history (skip closed tabs)
|
||||
var targetIndex = historyIndex - 1
|
||||
while targetIndex >= 0 {
|
||||
let tabId = tabHistory[targetIndex]
|
||||
if tabs.contains(where: { $0.id == tabId }) {
|
||||
isNavigatingHistory = true
|
||||
historyIndex = targetIndex
|
||||
selectedTabId = tabId
|
||||
isNavigatingHistory = false
|
||||
return
|
||||
}
|
||||
// Remove closed tab from history
|
||||
tabHistory.remove(at: targetIndex)
|
||||
historyIndex -= 1
|
||||
targetIndex -= 1
|
||||
}
|
||||
}
|
||||
|
||||
func navigateForward() {
|
||||
guard historyIndex < tabHistory.count - 1 else { return }
|
||||
|
||||
// Find the next valid tab in history (skip closed tabs)
|
||||
let targetIndex = historyIndex + 1
|
||||
while targetIndex < tabHistory.count {
|
||||
let tabId = tabHistory[targetIndex]
|
||||
if tabs.contains(where: { $0.id == tabId }) {
|
||||
isNavigatingHistory = true
|
||||
historyIndex = targetIndex
|
||||
selectedTabId = tabId
|
||||
isNavigatingHistory = false
|
||||
return
|
||||
}
|
||||
// Remove closed tab from history
|
||||
tabHistory.remove(at: targetIndex)
|
||||
// Don't increment targetIndex since we removed the element
|
||||
}
|
||||
}
|
||||
|
||||
var canNavigateBack: Bool {
|
||||
historyIndex > 0 && tabHistory.prefix(historyIndex).contains { tabId in
|
||||
tabs.contains { $0.id == tabId }
|
||||
}
|
||||
}
|
||||
|
||||
var canNavigateForward: Bool {
|
||||
historyIndex < tabHistory.count - 1 && tabHistory.suffix(from: historyIndex + 1).contains { tabId in
|
||||
tabs.contains { $0.id == tabId }
|
||||
}
|
||||
}
|
||||
|
||||
func newSplit(tabId: UUID, surfaceId: UUID, direction: SplitTree<TerminalSurface>.NewDirection) -> Bool {
|
||||
guard let tab = tabs.first(where: { $0.id == tabId }) else { return false }
|
||||
return tab.newSplit(from: surfaceId, direction: direction) != nil
|
||||
|
|
|
|||
30
scripts/setup.sh
Executable file
30
scripts/setup.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "==> Initializing submodules..."
|
||||
git submodule update --init --recursive
|
||||
|
||||
echo "==> Checking for zig..."
|
||||
if ! command -v zig &> /dev/null; then
|
||||
echo "Error: zig is not installed."
|
||||
echo "Install via: brew install zig"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Building GhosttyKit.xcframework (this may take a few minutes)..."
|
||||
cd ghostty
|
||||
zig build -Demit-xcframework=true -Doptimize=ReleaseFast
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "==> Creating symlink for GhosttyKit.xcframework..."
|
||||
ln -sf ghostty/macos/GhosttyKit.xcframework GhosttyKit.xcframework
|
||||
|
||||
echo "==> Setup complete!"
|
||||
echo ""
|
||||
echo "You can now build and run the app:"
|
||||
echo " ./scripts/reload.sh"
|
||||
Loading…
Add table
Add a link
Reference in a new issue