Merge branch 'ghostty-org:main' into invertCursor

This commit is contained in:
alienman5k
2025-04-27 15:50:59 -07:00
committed by GitHub
16 changed files with 184 additions and 93 deletions

View File

@ -353,15 +353,19 @@ jobs:
os:
[namespace-profile-ghostty-snap, namespace-profile-ghostty-snap-arm64]
runs-on: ${{ matrix.os }}
needs: test
needs: [test, build-dist]
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- uses: actions/checkout@v4
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@v4
with:
fetch-depth: 0
fetch-tags: true
name: source-tarball
- name: Extract tarball
run: |
mkdir dist
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
@ -371,6 +375,8 @@ jobs:
- run: sudo apt install -y udev
- run: sudo systemctl start systemd-udevd
- uses: snapcore/action-build@v1
with:
path: dist
build-windows:
runs-on: windows-2022

View File

@ -50,6 +50,8 @@ jobs:
if ! git diff --exit-code build.zig.zon; then
nix develop -c ./nix/build-support/check-zig-cache.sh --update
nix develop -c ./nix/build-support/check-zig-cache.sh
nix develop -c ./flatpak/build-support/check-zig-cache.sh --update
nix develop -c ./flatpak/build-support/check-zig-cache.sh
fi
# Verify the build still works. We choose an arbitrary build type

View File

@ -20,8 +20,8 @@
},
.z2d = .{
// vancluever/z2d
.url = "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
.hash = "z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C",
.url = "https://github.com/vancluever/z2d/archive/1bf4bc81819385f4b24596445c9a7cf3b3592b08.tar.gz",
.hash = "z2d-0.6.1-j5P_HlerCgBokMgrkl9QhJUKXuZWBGdPnH7cSXwv_ScW",
.lazy = true,
},
.zig_objc = .{
@ -103,8 +103,8 @@
// Other
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
.iterm2_themes = .{
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/c5e4212e763d652a422c22955cbd13038de1a356.tar.gz",
.hash = "N-V-__8AAIVuNwQhgzy1gME091DLGpUf4kDPd5zVEbxg-NVC",
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
.hash = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym",
.lazy = true,
},
},

12
build.zig.zon.json generated
View File

@ -54,10 +54,10 @@
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
},
"N-V-__8AAIVuNwQhgzy1gME091DLGpUf4kDPd5zVEbxg-NVC": {
"N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym": {
"name": "iterm2_themes",
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/c5e4212e763d652a422c22955cbd13038de1a356.tar.gz",
"hash": "sha256-SVvSI8gp1ANAUwIMp/v+/ZUiOZ4mPy4nQHlxzThI2fs="
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
"hash": "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0="
},
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
"name": "libpng",
@ -124,10 +124,10 @@
"url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",
"hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="
},
"z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C": {
"z2d-0.6.1-j5P_HlerCgBokMgrkl9QhJUKXuZWBGdPnH7cSXwv_ScW": {
"name": "z2d",
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
"hash": "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ="
"url": "https://github.com/vancluever/z2d/archive/1bf4bc81819385f4b24596445c9a7cf3b3592b08.tar.gz",
"hash": "sha256-wiJs6/LUiy+ApC5s7VPypbBukjBr4vjx3v/l9OrT70U="
},
"zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9": {
"name": "zf",

12
build.zig.zon.nix generated
View File

@ -170,11 +170,11 @@ in
};
}
{
name = "N-V-__8AAIVuNwQhgzy1gME091DLGpUf4kDPd5zVEbxg-NVC";
name = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym";
path = fetchZigArtifact {
name = "iterm2_themes";
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/c5e4212e763d652a422c22955cbd13038de1a356.tar.gz";
hash = "sha256-SVvSI8gp1ANAUwIMp/v+/ZUiOZ4mPy4nQHlxzThI2fs=";
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz";
hash = "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0=";
};
}
{
@ -282,11 +282,11 @@ in
};
}
{
name = "z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C";
name = "z2d-0.6.1-j5P_HlerCgBokMgrkl9QhJUKXuZWBGdPnH7cSXwv_ScW";
path = fetchZigArtifact {
name = "z2d";
url = "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz";
hash = "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ=";
url = "https://github.com/vancluever/z2d/archive/1bf4bc81819385f4b24596445c9a7cf3b3592b08.tar.gz";
hash = "sha256-wiJs6/LUiy+ApC5s7VPypbBukjBr4vjx3v/l9OrT70U=";
};
}
{

4
build.zig.zon.txt generated
View File

@ -27,8 +27,8 @@ https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/c5e4212e763d652a422c22955cbd13038de1a356.tar.gz
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz
https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz
https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz
https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz
https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz
https://github.com/vancluever/z2d/archive/1bf4bc81819385f4b24596445c9a7cf3b3592b08.tar.gz

View File

@ -67,9 +67,9 @@
},
{
"type": "archive",
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/c5e4212e763d652a422c22955cbd13038de1a356.tar.gz",
"dest": "vendor/p/N-V-__8AAIVuNwQhgzy1gME091DLGpUf4kDPd5zVEbxg-NVC",
"sha256": "495bd223c829d4034053020ca7fbfefd9522399e263f2e27407971cd3848d9fb"
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
"dest": "vendor/p/N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym",
"sha256": "572e66ba22778495dc3af9851cb86a73e0ef761ef8186ebebbf2fe12c6af0dbd"
},
{
"type": "archive",
@ -151,9 +151,9 @@
},
{
"type": "archive",
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
"dest": "vendor/p/z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C",
"sha256": "3c429549467ab5e45b0f28453d64a2b8149ee34a60af4915462bc863c0a7f074"
"url": "https://github.com/vancluever/z2d/archive/1bf4bc81819385f4b24596445c9a7cf3b3592b08.tar.gz",
"dest": "vendor/p/z2d-0.6.1-j5P_HlerCgBokMgrkl9QhJUKXuZWBGdPnH7cSXwv_ScW",
"sha256": "c2226cebf2d48b2f80a42e6ced53f2a5b06e92306be2f8f1deffe5f4ead3ef45"
},
{
"type": "archive",

View File

@ -357,6 +357,11 @@ typedef struct {
size_t len;
} ghostty_config_color_list_s;
// config.Palette
typedef struct {
ghostty_config_color_s colors[256];
} ghostty_config_palette_s;
// apprt.Target.Key
typedef enum {
GHOSTTY_TARGET_APP,

View File

@ -4,7 +4,7 @@ struct CommandOption: Identifiable, Hashable {
let id = UUID()
let title: String
let description: String?
let shortcut: String?
let symbols: [String]?
let action: () -> Void
static func == (lhs: CommandOption, rhs: CommandOption) -> Bool {
@ -21,8 +21,8 @@ struct CommandPaletteView: View {
var backgroundColor: Color = Color(nsColor: .windowBackgroundColor)
var options: [CommandOption]
@State private var query = ""
@State private var selectedIndex: UInt = 0
@State private var hoveredOptionID: UUID? = nil
@State private var selectedIndex: UInt?
@State private var hoveredOptionID: UUID?
// The options that we should show, taking into account any filtering from
// the query.
@ -35,7 +35,8 @@ struct CommandPaletteView: View {
}
var selectedOption: CommandOption? {
if selectedIndex < filteredOptions.count {
guard let selectedIndex else { return nil }
return if selectedIndex < filteredOptions.count {
filteredOptions[Int(selectedIndex)]
} else {
filteredOptions.last
@ -43,6 +44,12 @@ struct CommandPaletteView: View {
}
var body: some View {
let scheme: ColorScheme = if OSColor(backgroundColor).isLightColor {
.light
} else {
.dark
}
VStack(alignment: .leading, spacing: 0) {
CommandPaletteQuery(query: $query) { event in
switch (event) {
@ -54,23 +61,40 @@ struct CommandPaletteView: View {
selectedOption?.action()
case .move(.up):
if selectedIndex > 0 {
selectedIndex -= 1
}
if filteredOptions.isEmpty { break }
let current = selectedIndex ?? UInt(filteredOptions.count)
selectedIndex = (current == 0)
? UInt(filteredOptions.count - 1)
: current - 1
case .move(.down):
if selectedIndex < filteredOptions.count - 1 {
selectedIndex += 1
}
if filteredOptions.isEmpty { break }
let current = selectedIndex ?? UInt.max
selectedIndex = (current >= UInt(filteredOptions.count - 1))
? 0
: current + 1
case .move(_):
// Unknown, ignore
break
}
}
.onChange(of: query) { newValue in
// If the user types a query then we want to make sure the first
// value is selected. If the user clears the query and we were selecting
// the first, we unset any selection.
if !newValue.isEmpty {
if selectedIndex == nil {
selectedIndex = 0
}
} else {
if let selectedIndex, selectedIndex == 0 {
self.selectedIndex = nil
}
}
}
Divider()
.padding(.bottom, 4)
CommandTable(
options: filteredOptions,
@ -82,15 +106,23 @@ struct CommandPaletteView: View {
}
.frame(maxWidth: 500)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(backgroundColor)
.shadow(color: .black.opacity(0.4), radius: 10, x: 0, y: 10)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.black.opacity(0.1), lineWidth: 1)
)
ZStack {
Rectangle()
.fill(.ultraThinMaterial)
Rectangle()
.fill(backgroundColor)
.blendMode(.color)
}
.compositingGroup()
)
.clipShape(RoundedRectangle(cornerRadius: 10))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(nsColor: .tertiaryLabelColor).opacity(0.75))
)
.shadow(radius: 32, x: 0, y: 12)
.padding()
.environment(\.colorScheme, scheme)
}
}
@ -128,8 +160,9 @@ fileprivate struct CommandPaletteQuery: View {
TextField("Execute a command…", text: $query)
.padding()
.font(.system(size: 14))
.textFieldStyle(PlainTextFieldStyle())
.font(.system(size: 20, weight: .light))
.frame(height: 48)
.textFieldStyle(.plain)
.focused($isTextFieldFocused)
.onAppear {
isTextFieldFocused = true
@ -148,7 +181,7 @@ fileprivate struct CommandPaletteQuery: View {
fileprivate struct CommandTable: View {
var options: [CommandOption]
@Binding var selectedIndex: UInt
@Binding var selectedIndex: UInt?
@Binding var hoveredOptionID: UUID?
var action: (CommandOption) -> Void
@ -159,29 +192,34 @@ fileprivate struct CommandTable: View {
.padding()
} else {
ScrollViewReader { proxy in
ScrollView(showsIndicators: false) {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
ForEach(Array(options.enumerated()), id: \.1.id) { index, option in
CommandRow(
option: option,
isSelected: selectedIndex == index ||
(selectedIndex >= options.count &&
index == options.count - 1),
isSelected: {
if let selected = selectedIndex {
return selected == index ||
(selected >= options.count &&
index == options.count - 1)
} else {
return false
}
}(),
hoveredID: $hoveredOptionID
) {
action(option)
}
}
}
.padding(10)
}
.frame(maxHeight: 200)
.onChange(of: selectedIndex) { _ in
guard selectedIndex < options.count else { return }
withAnimation {
proxy.scrollTo(
options[Int(selectedIndex)].id,
anchor: .center)
}
guard let selectedIndex,
selectedIndex < options.count else { return }
proxy.scrollTo(
options[Int(selectedIndex)].id)
}
}
}
@ -200,20 +238,12 @@ fileprivate struct CommandRow: View {
HStack {
Text(option.title)
Spacer()
if let shortcut = option.shortcut {
Text(shortcut)
.font(.system(.body, design: .monospaced))
.kerning(1.5)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(
RoundedRectangle(cornerRadius: 6)
.fill(Color.gray.opacity(0.2))
)
if let symbols = option.symbols {
ShortcutSymbolsView(symbols: symbols)
.foregroundStyle(.secondary)
}
}
.padding(.horizontal, 6)
.padding(.vertical, 8)
.padding(8)
.background(
isSelected
? Color.accentColor.opacity(0.2)
@ -221,14 +251,26 @@ fileprivate struct CommandRow: View {
? Color.secondary.opacity(0.2)
: Color.clear)
)
.cornerRadius(6)
.cornerRadius(5)
}
.help(option.description ?? "")
.buttonStyle(PlainButtonStyle())
.buttonStyle(.plain)
.onHover { hovering in
hoveredID = hovering ? option.id : nil
}
.padding(.horizontal, 4)
.padding(.vertical, 1)
}
}
/// A row of Text representing a shortcut.
fileprivate struct ShortcutSymbolsView: View {
let symbols: [String]
var body: some View {
HStack(spacing: 1) {
ForEach(symbols, id: \.self) { symbol in
Text(symbol)
.frame(minWidth: 13)
}
}
}
}

View File

@ -40,7 +40,7 @@ struct TerminalCommandPaletteView: View {
return CommandOption(
title: String(cString: c.title),
description: String(cString: c.description),
shortcut: ghosttyConfig.keyboardShortcut(for: action)?.description
symbols: ghosttyConfig.keyboardShortcut(for: action)?.keyList
) {
onAction(action)
}

View File

@ -1,12 +1,9 @@
import SwiftUI
extension KeyboardShortcut: @retroactive CustomStringConvertible {
public var description: String {
var result = ""
public var keyList: [String] {
var result: [String] = []
if modifiers.contains(.command) {
result.append("")
}
if modifiers.contains(.control) {
result.append("")
}
@ -16,6 +13,9 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
if modifiers.contains(.shift) {
result.append("")
}
if modifiers.contains(.command) {
result.append("")
}
let keyString: String
switch key {
@ -24,10 +24,14 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
case .delete: keyString = ""
case .space: keyString = ""
case .tab: keyString = ""
case .upArrow: keyString = ""
case .downArrow: keyString = ""
case .leftArrow: keyString = ""
case .rightArrow: keyString = ""
case .upArrow: keyString = ""
case .downArrow: keyString = ""
case .leftArrow: keyString = ""
case .rightArrow: keyString = ""
case .pageUp: keyString = ""
case .pageDown: keyString = ""
case .home: keyString = ""
case .end: keyString = ""
default:
keyString = String(key.character.uppercased())
}
@ -35,6 +39,10 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
result.append(keyString)
return result
}
public var description: String {
return self.keyList.joined()
}
}
// This is available in macOS 14 so this only applies to early macOS versions.

View File

@ -70,7 +70,6 @@ parts:
plugin: nil
build-attributes: [enable-patchelf]
build-packages:
- blueprint-compiler
- libgtk-4-dev
- libadwaita-1-dev
# TODO: Add when the Snap is updated to Ubuntu 24.10+
@ -80,7 +79,7 @@ parts:
- patchelf
- gettext
override-build: |
craftctl set version=$(git describe --abbrev=8)
craftctl set version=$(cat VERSION)
$CRAFT_PART_SRC/../../zig/src/zig build -Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR -Doptimize=ReleaseFast
cp -rp zig-out/* $CRAFT_PART_INSTALL/
sed -i 's|Icon=com.mitchellh.ghostty|Icon=/snap/ghostty/current/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png|g' $CRAFT_PART_INSTALL/share/applications/com.mitchellh.ghostty.desktop

View File

@ -36,6 +36,17 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
"--format=tgz",
});
// embed the Ghostty version in the tarball
{
const version = b.addWriteFiles().add("VERSION", b.fmt("{}", .{cfg.version}));
// --add-file uses the most recent --prefix to determine the path
// in the archive to copy the file (the directory only).
git_archive.addArg(b.fmt("--prefix=ghostty-{}/", .{
cfg.version,
}));
git_archive.addPrefixedFileArg("--add-file=", version);
}
// Add all of our resources into the tarball.
for (resources.items) |resource| {
// Our dist path basename may not match our generated file basename,

View File

@ -3934,6 +3934,24 @@ pub const Palette = struct {
/// The actual value that is updated as we parse.
value: terminal.color.Palette = terminal.color.default,
/// ghostty_config_palette_s
pub const C = extern struct {
colors: [265]Color.C,
};
pub fn cval(self: Self) Palette.C {
var result: Palette.C = undefined;
for (self.value, 0..) |color, i| {
result.colors[i] = Color.C{
.r = color.r,
.g = color.g,
.b = color.b,
};
}
return result;
}
pub fn parseCLI(
self: *Self,
input: ?[]const u8,

View File

@ -2239,7 +2239,7 @@ fn draw_branch_node(
@min(float_width - cx, float_height - cy),
);
var ctx = canvas.getContext() catch return;
var ctx = canvas.getContext();
defer ctx.deinit();
ctx.setSource(.{ .opaque_pattern = .{
.pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } },
@ -2290,7 +2290,7 @@ fn draw_circle(
};
const r: f64 = 0.5 * @min(float_width, float_height);
var ctx = canvas.getContext() catch return;
var ctx = canvas.getContext();
defer ctx.deinit();
ctx.setSource(.{ .opaque_pattern = .{
.pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } },
@ -2680,7 +2680,7 @@ fn draw_arc(
// Fraction away from the center to place the middle control points,
const s: f64 = 0.25;
var ctx = try canvas.getContext();
var ctx = canvas.getContext();
defer ctx.deinit();
ctx.setSource(.{ .opaque_pattern = .{
.pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } },
@ -2974,7 +2974,7 @@ fn draw_separated_block_quadrant(self: Box, canvas: *font.sprite.Canvas, comptim
}
}
var ctx = try canvas.getContext();
var ctx = canvas.getContext();
defer ctx.deinit();
ctx.setSource(.{ .opaque_pattern = .{
.pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } },

View File

@ -149,8 +149,8 @@ pub const Canvas = struct {
}
/// Acquires a z2d drawing context, caller MUST deinit context.
pub fn getContext(self: *Canvas) Allocator.Error!z2d.Context {
return try z2d.Context.init(self.alloc, &self.sfc);
pub fn getContext(self: *Canvas) z2d.Context {
return z2d.Context.init(self.alloc, &self.sfc);
}
/// Draw and fill a single pixel