From 95d9b1e627abed0543806cc6f1abad3e1d60384b Mon Sep 17 00:00:00 2001 From: Kat <65649991+00-kat@users.noreply.github.com> Date: Fri, 4 Jul 2025 07:24:55 +0000 Subject: [PATCH 1/8] Request translators to update the CODEOWNERS file. --- po/README_TRANSLATORS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/po/README_TRANSLATORS.md b/po/README_TRANSLATORS.md index ca1e45faa..c02a5bd48 100644 --- a/po/README_TRANSLATORS.md +++ b/po/README_TRANSLATORS.md @@ -148,6 +148,18 @@ const locales = [_][]const u8{ You should then be able to run `zig build` and see your translations in action. +Before opening a pull request with the new translation file, you should also add +your locale to the `CODEOWNERS` file. Find the `# Localization` section near the +bottom and add a line like so (where `xx_YY` is your locale): + +```diff + # Localization + /po/README_TRANSLATORS.md @ghostty-org/localization + /po/com.mitchellh.ghostty.pot @ghostty-org/localization + /po/zh_CN.UTF-8.po @ghostty-org/zh_CN ++/po/xx_YY.UTF-8.po @ghostty-org/xx_YY +``` + ## Style Guide These are general style guidelines for translations. Naturally, the specific From d8838cff0b1b1d9170568fa3fc99c69cc1f050a3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Jul 2025 07:30:24 -0700 Subject: [PATCH 2/8] macOS: remove @DeferredProperty usage from TerminalEntity This fixes an Apple Shortcuts crash for macOS 15 and earlier. Unfortunately it looks like we can't guard these with `@available`. I'm going to report an Apple Feedback about this but for now this gets shortcuts working on macOS 15 and earlier. --- .../App Intents/Entities/TerminalEntity.swift | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/macos/Sources/Features/App Intents/Entities/TerminalEntity.swift b/macos/Sources/Features/App Intents/Entities/TerminalEntity.swift index e29fbba3f..cc3b9f63a 100644 --- a/macos/Sources/Features/App Intents/Entities/TerminalEntity.swift +++ b/macos/Sources/Features/App Intents/Entities/TerminalEntity.swift @@ -14,26 +14,6 @@ struct TerminalEntity: AppEntity { @Property(title: "Kind") var kind: Kind - @MainActor - @DeferredProperty(title: "Full Contents") - @available(macOS 26.0, *) - var screenContents: String? { - get async { - guard let surfaceView else { return nil } - return surfaceView.cachedScreenContents.get() - } - } - - @MainActor - @DeferredProperty(title: "Visible Contents") - @available(macOS 26.0, *) - var visibleContents: String? { - get async { - guard let surfaceView else { return nil } - return surfaceView.cachedVisibleContents.get() - } - } - var screenshot: Image? static var typeDisplayRepresentation: TypeDisplayRepresentation { From eea073c97bd9c4de15a7179c1ad3bb252652fbfa Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Jul 2025 07:40:59 -0700 Subject: [PATCH 3/8] On wait-after-command (or abnormal exit), only close on encoded key Fixes #7794 This commit also resets some terminal state to give us a better chance of getting an encoded key, such as ensuring keyboard input is enabled and disabling any Kitty protocols. This shouldn't ever be set but just in case! --- src/Surface.zig | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index dc7b0e3bf..6d0f1584b 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1034,6 +1034,12 @@ fn childExited(self: *Surface, info: apprt.surface.Message.ChildExited) void { t.printString("Process exited. Press any key to close the terminal.") catch break :terminal; t.modes.set(.cursor_visible, false); + + // We also want to ensure that normal keyboard encoding is on + // so that we can close the terminal. We close the terminal on + // any key press that encodes a character. + t.modes.set(.disable_keyboard, false); + t.screen.kitty_keyboard.set(.set, .{}); } // Waiting after command we stop here. The terminal is updated, our @@ -2129,14 +2135,6 @@ pub fn keyCallback( if (self.io.terminal.modes.get(.disable_keyboard)) return .consumed; } - // If our process is exited and we press a key then we close the - // surface. We may want to eventually move this to the apprt rather - // than in core. - if (self.child_exited and event.action == .press) { - self.close(); - return .closed; - } - // If this input event has text, then we hide the mouse if configured. // We only do this on pressed events to avoid hiding the mouse when we // change focus due to a keybinding (i.e. switching tabs). @@ -2231,6 +2229,14 @@ pub fn keyCallback( event, if (insp_ev) |*ev| ev else null, )) |write_req| { + // If our process is exited and we press a key that results in + // an encoded value, we close the surface. We want to eventually + // move this behavior to the apprt probably. + if (self.child_exited) { + self.close(); + return .closed; + } + errdefer write_req.deinit(); self.io.queueMessage(switch (write_req) { .small => |v| .{ .write_small = v }, From de3e77570e479033853733736a025ae05100f6bd Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Jul 2025 18:26:27 -0400 Subject: [PATCH 4/8] add second cursor list --- src/renderer/cell.zig | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/renderer/cell.zig b/src/renderer/cell.zig index ef7122699..3e5ae92c0 100644 --- a/src/renderer/cell.zig +++ b/src/renderer/cell.zig @@ -103,11 +103,12 @@ pub const Contents = struct { // form a single grapheme, and multi-substitutions in fonts, the number // of glyphs in a row is theoretically unlimited. // - // We have size.rows + 1 lists because index 0 is used for a special - // list containing the cursor cell which needs to be first in the buffer. + // We have size.rows + 2 lists because indexes 0 and size.rows - 1 are + // used for special lists containing the cursor cell which need to + // be first and last in the buffer, respectively. var fg_rows = try ArrayListCollection(shaderpkg.CellText).init( alloc, - size.rows + 1, + size.rows + 2, size.columns * 3, ); errdefer fg_rows.deinit(alloc); @@ -118,14 +119,19 @@ pub const Contents = struct { self.bg_cells = bg_cells; self.fg_rows = fg_rows; - // We don't need 3*cols worth of cells for the cursor list, so we can - // replace it with a smaller list. This is technically a tiny bit of + // We don't need 3*cols worth of cells for the cursor lists, so we can + // replace them with smaller lists. This is technically a tiny bit of // extra work but resize is not a hot function so it's worth it to not // waste the memory. self.fg_rows.lists[0].deinit(alloc); self.fg_rows.lists[0] = try std.ArrayListUnmanaged( shaderpkg.CellText, ).initCapacity(alloc, 1); + + self.fg_rows.lists[size.rows + 1].deinit(alloc); + self.fg_rows.lists[size.rows + 1] = try std.ArrayListUnmanaged( + shaderpkg.CellText, + ).initCapacity(alloc, 1); } /// Reset the cell contents to an empty state without resizing. @@ -137,6 +143,7 @@ pub const Contents = struct { /// Set the cursor value. If the value is null then the cursor is hidden. pub fn setCursor(self: *Contents, v: ?shaderpkg.CellText) void { self.fg_rows.lists[0].clearRetainingCapacity(); + self.fg_rows.lists[self.size.rows + 1].clearRetainingCapacity(); if (v) |cell| { self.fg_rows.lists[0].appendAssumeCapacity(cell); From a9fc3b6fa0f862f00825664c44e6dcd8305e2141 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Jul 2025 18:50:23 -0400 Subject: [PATCH 5/8] enable drawing cursor on top or bottom based on style --- src/renderer/cell.zig | 17 ++++++++++++----- src/renderer/generic.zig | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/renderer/cell.zig b/src/renderer/cell.zig index 3e5ae92c0..821ac1c7c 100644 --- a/src/renderer/cell.zig +++ b/src/renderer/cell.zig @@ -141,12 +141,19 @@ pub const Contents = struct { } /// Set the cursor value. If the value is null then the cursor is hidden. - pub fn setCursor(self: *Contents, v: ?shaderpkg.CellText) void { + pub fn setCursor(self: *Contents, v: ?shaderpkg.CellText, cursor_style: ?renderer.CursorStyle) void { self.fg_rows.lists[0].clearRetainingCapacity(); self.fg_rows.lists[self.size.rows + 1].clearRetainingCapacity(); if (v) |cell| { - self.fg_rows.lists[0].appendAssumeCapacity(cell); + if (cursor_style) |style| { + switch (style) { + // Block cursors should be drawn first + .block => self.fg_rows.lists[0].appendAssumeCapacity(cell), + // Other cursor styles should be drawn last + .block_hollow, .bar, .underline, .lock => self.fg_rows.lists[self.size.rows + 1].appendAssumeCapacity(cell), + } + } } } @@ -374,17 +381,17 @@ test Contents { } } - // Add a cursor. + // Add a block cursor. const cursor_cell: shaderpkg.CellText = .{ .mode = .cursor, .grid_pos = .{ 2, 3 }, .color = .{ 0, 0, 0, 1 }, }; - c.setCursor(cursor_cell); + c.setCursor(cursor_cell, .block); try testing.expectEqual(cursor_cell, c.fg_rows.lists[0].items[0]); // And remove it. - c.setCursor(null); + c.setCursor(null, null); try testing.expectEqual(0, c.fg_rows.lists[0].items.len); } diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index e7faf633f..3a65b9ac5 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -2791,7 +2791,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { // Setup our cursor rendering information. cursor: { // By default, we don't handle cursor inversion on the shader. - self.cells.setCursor(null); + self.cells.setCursor(null, null); self.uniforms.cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16), @@ -3162,7 +3162,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { @intCast(render.glyph.offset_x), @intCast(render.glyph.offset_y), }, - }); + }, cursor_style); } fn addPreeditCell( From 0b4a1e21540b8368507bd0f4e0ef2eb328ce670a Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Jul 2025 18:54:55 -0400 Subject: [PATCH 6/8] added test for other cursor style --- src/renderer/cell.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/renderer/cell.zig b/src/renderer/cell.zig index 821ac1c7c..083adee40 100644 --- a/src/renderer/cell.zig +++ b/src/renderer/cell.zig @@ -393,6 +393,10 @@ test Contents { // And remove it. c.setCursor(null, null); try testing.expectEqual(0, c.fg_rows.lists[0].items.len); + + // Add a hollow cursor. + c.setCursor(cursor_cell, .block_hollow); + try testing.expectEqual(cursor_cell, c.fg_rows.lists[rows + 1].items[0]); } test "Contents clear retains other content" { From b8931dd1db69421bd747059457d1de8f9531f346 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Fri, 4 Jul 2025 16:08:28 -0400 Subject: [PATCH 7/8] bash: stop using PS0 for the 'cursor' feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our use of PS0 (which bash runs before command execution) was causing raw command sequences to be printed between multiple commands in a sequence. $ alias garbage='echo start > echo end' $ garbage start �\���dend I wasn't able to definitely track down all of the reasons for why this only happens in the command sequence case, but I suspect it's related to the way that __ghostty_preexec runs from within the bash DEBUG trap (by way of bash-preexec). This problem occurs when PS0 is set to _any_ string (even "") inside of __ghostty_preexec, which also rules out most/any Ghostty-specific code. PS1 and PS2 appear to be safe to (re)set in this context. Fortunately, we can avoid using PS0 entirely by instead printing the cursor reset escape sequence directly from __ghostty_precmd because it also runs just before command execution. --- src/shell-integration/bash/ghostty.bash | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash index 0766198f9..21a6965ca 100644 --- a/src/shell-integration/bash/ghostty.bash +++ b/src/shell-integration/bash/ghostty.bash @@ -106,7 +106,6 @@ _ghostty_last_reported_cwd="" function __ghostty_precmd() { local ret="$?" if test "$_ghostty_executing" != "0"; then - _GHOSTTY_SAVE_PS0="$PS0" _GHOSTTY_SAVE_PS1="$PS1" _GHOSTTY_SAVE_PS2="$PS2" @@ -123,8 +122,8 @@ function __ghostty_precmd() { # Cursor if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then - PS1=$PS1'\[\e[5 q\]' - PS0=$PS0'\[\e[0 q\]' + PS1=$PS1'\[\e[5 q\]' # blinking bar for input + builtin printf "\e[0 q" # reset to default cursor fi # Title (working directory) @@ -154,7 +153,6 @@ function __ghostty_precmd() { function __ghostty_preexec() { builtin local cmd="$1" - PS0="$_GHOSTTY_SAVE_PS0" PS1="$_GHOSTTY_SAVE_PS1" PS2="$_GHOSTTY_SAVE_PS2" From 1a4b128af336d497deab540ec976db7f7926064e Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Jul 2025 21:36:17 -0400 Subject: [PATCH 8/8] replace nested if for readability --- src/renderer/cell.zig | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/renderer/cell.zig b/src/renderer/cell.zig index 083adee40..97b24aa90 100644 --- a/src/renderer/cell.zig +++ b/src/renderer/cell.zig @@ -145,15 +145,14 @@ pub const Contents = struct { self.fg_rows.lists[0].clearRetainingCapacity(); self.fg_rows.lists[self.size.rows + 1].clearRetainingCapacity(); - if (v) |cell| { - if (cursor_style) |style| { - switch (style) { - // Block cursors should be drawn first - .block => self.fg_rows.lists[0].appendAssumeCapacity(cell), - // Other cursor styles should be drawn last - .block_hollow, .bar, .underline, .lock => self.fg_rows.lists[self.size.rows + 1].appendAssumeCapacity(cell), - } - } + const cell = v orelse return; + const style = cursor_style orelse return; + + switch (style) { + // Block cursors should be drawn first + .block => self.fg_rows.lists[0].appendAssumeCapacity(cell), + // Other cursor styles should be drawn last + .block_hollow, .bar, .underline, .lock => self.fg_rows.lists[self.size.rows + 1].appendAssumeCapacity(cell), } }