Skip to content

Commit 750682c

Browse files
committed
Fix project pill layout and tabs
1 parent 56d4386 commit 750682c

38 files changed

Lines changed: 2525 additions & 372 deletions

.github/workflows/release-macos-app.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,87 @@ jobs:
109109
cp -R "$RELEASE_DIR/binaries" "$BUNDLE_DIR/binaries"
110110
fi
111111
112+
- name: Install Zig (0.15.2)
113+
run: |
114+
set -euo pipefail
115+
ZIG_VERSION="0.15.2"
116+
117+
HOST_ARCH="$(uname -m)"
118+
if [ "$HOST_ARCH" = "arm64" ] || [ "$HOST_ARCH" = "aarch64" ]; then
119+
ZIG_ARCH="aarch64"
120+
elif [ "$HOST_ARCH" = "x86_64" ] || [ "$HOST_ARCH" = "amd64" ]; then
121+
ZIG_ARCH="x86_64"
122+
else
123+
echo "Unsupported host arch: $HOST_ARCH"
124+
exit 1
125+
fi
126+
127+
URL="https://ziglang.org/download/${ZIG_VERSION}/zig-macos-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz"
128+
curl -fsSL "$URL" -o "$RUNNER_TEMP/zig.tar.xz"
129+
mkdir -p "$RUNNER_TEMP/zig"
130+
tar -xf "$RUNNER_TEMP/zig.tar.xz" -C "$RUNNER_TEMP/zig"
131+
132+
ZIG_BIN="$(find "$RUNNER_TEMP/zig" -maxdepth 2 -type f -name zig | head -n 1)"
133+
if [ -z "$ZIG_BIN" ]; then
134+
echo "Failed to locate zig binary after extracting."
135+
exit 1
136+
fi
137+
138+
echo "$(dirname "$ZIG_BIN")" >> "$GITHUB_PATH"
139+
zig version
140+
141+
- name: Build Ghostty VT dylib (universal)
142+
run: |
143+
set -euo pipefail
144+
145+
VENDOR_DIR="apps/macos/vendor/ghostty"
146+
BRIDGE_DIR="apps/macos/Experiments/GhosttyVTBridge"
147+
OUT_DIR="apps/macos/App/GhosttyVT/ghostty/lib"
148+
OUT_LIB="$OUT_DIR/libhack_ghostty_vt.dylib"
149+
150+
if [ ! -d "$VENDOR_DIR" ]; then
151+
echo "Missing vendor ghostty dir: $VENDOR_DIR"
152+
exit 1
153+
fi
154+
155+
mkdir -p "$OUT_DIR"
156+
157+
# Patch Ghostty's lib_vt.zig for our C ABI bridge.
158+
python3 - <<'PY'
159+
import pathlib
160+
161+
lib_vt = pathlib.Path("apps/macos/vendor/ghostty/src/lib_vt.zig")
162+
text = lib_vt.read_text(encoding="utf-8")
163+
guard = 'if (@import("root") == lib) {'
164+
patched = 'if (@import("root") == lib and terminal.options.c_abi) {'
165+
if guard in text and patched not in text:
166+
text = text.replace(guard, patched)
167+
if "@export(&" in text:
168+
text = text.replace("@export(&", "@export(")
169+
lib_vt.write_text(text, encoding="utf-8")
170+
PY
171+
172+
mkdir -p "$RUNNER_TEMP/ghosttyvt/arm64" "$RUNNER_TEMP/ghosttyvt/x86_64"
173+
174+
pushd "$BRIDGE_DIR" >/dev/null
175+
GHOSTTY_DIR="$GITHUB_WORKSPACE/$VENDOR_DIR"
176+
177+
zig build -Dghostty="$GHOSTTY_DIR" -Doptimize=ReleaseSafe -Dtarget=aarch64-macos --prefix "$RUNNER_TEMP/ghosttyvt/arm64"
178+
zig build -Dghostty="$GHOSTTY_DIR" -Doptimize=ReleaseSafe -Dtarget=x86_64-macos --prefix "$RUNNER_TEMP/ghosttyvt/x86_64"
179+
popd >/dev/null
180+
181+
ARM_LIB="$RUNNER_TEMP/ghosttyvt/arm64/lib/libhack_ghostty_vt.dylib"
182+
X64_LIB="$RUNNER_TEMP/ghosttyvt/x86_64/lib/libhack_ghostty_vt.dylib"
183+
184+
if [ ! -f "$ARM_LIB" ] || [ ! -f "$X64_LIB" ]; then
185+
echo "Missing built libs: $ARM_LIB / $X64_LIB"
186+
ls -la "$RUNNER_TEMP/ghosttyvt/arm64/lib" || true
187+
ls -la "$RUNNER_TEMP/ghosttyvt/x86_64/lib" || true
188+
exit 1
189+
fi
190+
191+
lipo -create "$ARM_LIB" "$X64_LIB" -output "$OUT_LIB"
192+
112193
- name: Install xcodegen
113194
run: brew install xcodegen
114195

@@ -185,6 +266,23 @@ jobs:
185266
186267
codesign -vvv "$BUNDLED_HACK"
187268
269+
- name: Codesign Ghostty VT dylib
270+
env:
271+
GHOSTTY_VT: apps/macos/App/GhosttyVT/ghostty/lib/libhack_ghostty_vt.dylib
272+
run: |
273+
set -euo pipefail
274+
if [ ! -f "$GHOSTTY_VT" ]; then
275+
echo "Missing Ghostty VT dylib: $GHOSTTY_VT"
276+
exit 1
277+
fi
278+
279+
codesign --force --options runtime --timestamp \
280+
--sign "$CODESIGN_IDENTITY_HASH" \
281+
--keychain "$KEYCHAIN_PATH" \
282+
"$GHOSTTY_VT"
283+
284+
codesign -vvv "$GHOSTTY_VT"
285+
188286
- name: Build and archive
189287
working-directory: ${{ env.PROJECT_PATH }}
190288
env:
855 KB
Binary file not shown.
899 KB
Binary file not shown.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Placeholder file.
2+
3+
This directory is overwritten during release builds when the macOS app is populated with the
4+
bundled CLI assets. Keeping a real file here avoids stale Xcode project references failing local
5+
builds if a previously-generated project pointed at a file under `BundledCLI/assets/`.
6+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
This directory is populated during builds with `libhack_ghostty_vt.dylib`.
2+
3+
- Release CI builds a universal dylib and stages it here before `xcodebuild archive`.
4+
- The app copies this folder into `Hack Desktop.app/Contents/Resources/ghostty/` during the build.
5+
6+
If you're building locally and want the embedded terminal to work, run:
7+
8+
`bun run macos:ghostty:bundle`
9+
1.77 MB
Binary file not shown.

apps/macos/App/HackDesktopApp.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import SwiftUI
33
import DashboardFeature
44
import HackCLIService
55

6+
#if os(macOS)
7+
import AppKit
8+
#endif
9+
610
#if RELEASE
711
import Sparkle
812
#endif
@@ -101,6 +105,13 @@ private struct DashboardCommands: Commands {
101105
NotificationCenter.default.post(name: .hackCommandPaletteRequested, object: nil)
102106
}
103107
.keyboardShortcut("k", modifiers: .command)
108+
109+
Button("Toggle Sidebar") {
110+
#if os(macOS)
111+
NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil)
112+
#endif
113+
}
114+
.keyboardShortcut("b", modifiers: .command)
104115
}
105116
}
106117
}

apps/macos/Experiments/GhosttyVTBridge/build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub fn build(b: *std.Build) !void {
8484
const uucode = b.dependency("uucode", .{
8585
.build_config_path = std.Build.LazyPath{ .cwd_relative = uucode_config_path },
8686
});
87+
ghostty_vt_impl.addImport("uucode", uucode.module("uucode"));
8788
const unicode_tables = try UnicodeTables.init(b, ghostty_path, uucode);
8889
unicode_tables.addModuleImport(ghostty_vt_impl);
8990

apps/macos/Experiments/GhosttyVTBridge/src/bridge.zig

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ export fn hack_ghostty_vt_create(cols: u32, rows: u32) ?*TerminalHandle {
5555
const alloc = std.heap.c_allocator;
5656
var handle = alloc.create(TerminalHandle) catch return null;
5757
handle.alloc = alloc;
58-
handle.terminal = ghostty_vt.Terminal.init(alloc, .{ .cols = cols_u16, .rows = rows_u16 }) catch {
58+
// Keep a generous scrollback buffer so app tabs feel like a real terminal.
59+
// (Ghostty's default is 10k lines; that's too shallow for logs-heavy workflows.)
60+
handle.terminal = ghostty_vt.Terminal.init(alloc, .{
61+
.cols = cols_u16,
62+
.rows = rows_u16,
63+
.max_scrollback = 200_000,
64+
}) catch {
5965
alloc.destroy(handle);
6066
return null;
6167
};
@@ -85,6 +91,24 @@ export fn hack_ghostty_vt_feed(handle: ?*TerminalHandle, bytes: [*]const u8, len
8591
_ = handle.?.stream.nextSlice(bytes[0..len]) catch {};
8692
}
8793

94+
/// Scroll the viewport within the terminal scrollback buffer.
95+
///
96+
/// Positive values scroll down, negative values scroll up.
97+
export fn hack_ghostty_vt_scroll_viewport_delta(handle: ?*TerminalHandle, delta_rows: i32) void {
98+
if (handle == null) return;
99+
handle.?.terminal.scrollViewport(.{ .delta = @as(isize, delta_rows) }) catch {};
100+
}
101+
102+
export fn hack_ghostty_vt_scroll_viewport_top(handle: ?*TerminalHandle) void {
103+
if (handle == null) return;
104+
handle.?.terminal.scrollViewport(.top) catch {};
105+
}
106+
107+
export fn hack_ghostty_vt_scroll_viewport_bottom(handle: ?*TerminalHandle) void {
108+
if (handle == null) return;
109+
handle.?.terminal.scrollViewport(.bottom) catch {};
110+
}
111+
88112
export fn hack_ghostty_vt_plain_string(handle: ?*TerminalHandle, out_len: ?*usize) ?[*]u8 {
89113
if (handle == null) return null;
90114
const str = handle.?.terminal.plainString(handle.?.alloc) catch return null;

apps/macos/Packages/Features/DashboardFeature/Sources/DashboardFeature/AdaptiveStyles.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,10 @@ extension View {
119119
if #available(macOS 26, *) {
120120
self
121121
.background(.clear)
122+
// Keep the toolbar visible, but visually clear. We tune the underlying NSToolbar to avoid
123+
// per-item "pill" backplates (see WindowToolbarTuner).
122124
.toolbarBackground(.clear, for: .windowToolbar)
123-
.toolbarBackgroundVisibility(.hidden, for: .windowToolbar)
125+
.toolbarBackgroundVisibility(.visible, for: .windowToolbar)
124126
} else {
125127
self
126128
.background(.ultraThinMaterial)

0 commit comments

Comments
 (0)