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 { 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 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 }, diff --git a/src/renderer/cell.zig b/src/renderer/cell.zig index ef7122699..97b24aa90 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. @@ -135,11 +141,18 @@ 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); + 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), } } @@ -367,18 +380,22 @@ 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); + + // 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" { 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( diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash index 6016e9096..f13a7d378 100644 --- a/src/shell-integration/bash/ghostty.bash +++ b/src/shell-integration/bash/ghostty.bash @@ -270,7 +270,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" @@ -287,8 +286,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) @@ -318,7 +317,6 @@ function __ghostty_precmd() { function __ghostty_preexec() { builtin local cmd="$1" - PS0="$_GHOSTTY_SAVE_PS0" PS1="$_GHOSTTY_SAVE_PS1" PS2="$_GHOSTTY_SAVE_PS2"