Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,14 @@ pub fn build(b: *std.Build) void {
b.installDirectory(.{
.source_dir = b.path("resources/shell-integration"),
.install_dir = .prefix,
.install_subdir = "share/shell-integration",
.install_subdir = "share/seance/shell-integration",
});

// Install wrapper scripts (claude wrapper, etc.)
b.installDirectory(.{
.source_dir = b.path("resources/bin"),
.install_dir = .prefix,
.install_subdir = "share/bin",
.install_subdir = "share/seance/bin",
});

// Install bundled icons (from GNOME Icon Library) into hicolor theme
Expand Down
1 change: 1 addition & 0 deletions src/command_palette.zig
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const static_commands = [_]Command{
.{ .label = "Jump to Unread", .action = .jump_to_unread, .keywords = "jump unread notification" },
.{ .label = "Open Settings", .action = .open_settings, .keywords = "settings preferences config options" },
.{ .label = "Reload Configuration", .action = .reload_config, .keywords = "config reload refresh settings" },
.{ .label = "Toggle Fullscreen", .action = .toggle_fullscreen, .keywords = "fullscreen toggle window maximize f11" },
.{ .label = "Quit Seance", .action = .quit_app, .keywords = "quit exit close application app shutdown" },

// Workspace commands
Expand Down
15 changes: 15 additions & 0 deletions src/ghostty_bridge.zig
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,21 @@ fn createWrapperResourcesDir() bool {
}
}

// Symlink ghostty's .zshenv — ghostty sets ZDOTDIR to this directory so
// zsh auto-sources .zshenv, which restores the real ZDOTDIR and chains
// into ghostty-integration. Without this symlink the wrapper directory
// has no .zshenv and zsh shows the new-user wizard.
{
var zbuf: [std.fs.max_path_bytes]u8 = undefined;
const ztarget = std.fmt.bufPrint(&zbuf, "{s}/shell-integration/zsh/.zshenv", .{real}) catch return false;
var zsh_dir = wd.openDir("shell-integration/zsh", .{}) catch return false;
defer zsh_dir.close();
zsh_dir.symLink(ztarget, ".zshenv", .{}) catch |e| {
std.log.warn("ghostty_bridge: failed to symlink .zshenv: {}", .{e});
return false;
};
}

// Write wrapper shell integration scripts
writeShellWrapper(wd, "shell-integration/bash/ghostty.bash", real, "/shell-integration/bash/ghostty.bash", "/bash-integration.sh") catch return false;
writeShellWrapper(wd, "shell-integration/zsh/ghostty-integration", real, "/shell-integration/zsh/ghostty-integration", "/zsh-integration.sh") catch return false;
Expand Down
3 changes: 3 additions & 0 deletions src/keybinds.zig
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub const Action = enum(u8) {

// Window
new_window,
toggle_fullscreen,
quit_app,

// Command palette
Expand Down Expand Up @@ -260,6 +261,7 @@ fn initDefaults() void {

// Window
set(.new_window, .{ .key = c.GDK_KEY_N, .ctrl = true, .shift = true });
set(.toggle_fullscreen, .{ .key = c.GDK_KEY_F11 });
set(.quit_app, .{ .key = c.GDK_KEY_Q, .ctrl = true, .shift = true });

// Command palette
Expand Down Expand Up @@ -547,6 +549,7 @@ pub fn executeAction(action: Action, state: *Window.WindowState) c.gboolean {
.new_window => {
_ = state.window_manager.newWindow();
},
.toggle_fullscreen => state.toggleFullscreen(),
.quit_app => state.quitApp(),

// Command palette
Expand Down
4 changes: 2 additions & 2 deletions src/pane.zig
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,10 @@ fn initSurface(pane: *Pane, width: u32, height: u32) void {
if (std.fs.selfExePath(&exe_buf)) |exe_path| {
if (std.fs.path.dirname(exe_path)) |exe_dir| {
if (std.fs.path.dirname(exe_dir)) |prefix| {
if (std.fmt.bufPrintZ(&int_env_val_buf, "{s}/share/shell-integration", .{prefix})) |iv| {
if (std.fmt.bufPrintZ(&int_env_val_buf, "{s}/share/seance/shell-integration", .{prefix})) |iv| {
int_val = iv;
} else |_| {}
if (std.fmt.bufPrintZ(&bin_env_val_buf, "{s}/share/bin", .{prefix})) |bv| {
if (std.fmt.bufPrintZ(&bin_env_val_buf, "{s}/share/seance/bin", .{prefix})) |bv| {
bin_val = bv;
} else |_| {}
}
Expand Down
1 change: 1 addition & 0 deletions src/shortcuts_overlay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const groups = [_]Group{
.title = "General",
.rows = &.{
.{ .single = .{ .action = .new_window, .label = "New Window" } },
.{ .single = .{ .action = .toggle_fullscreen, .label = "Fullscreen" } },
.{ .single = .{ .action = .open_command_palette, .label = "Command Palette" } },
.{ .single = .{ .action = .open_folder, .label = "Open Folder" } },
.{ .single = .{ .action = .open_settings, .label = "Settings" } },
Expand Down
26 changes: 26 additions & 0 deletions src/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,15 @@ pub const WindowState = struct {
/// `confirm_close_window` is enabled and there's interesting state to
/// warn about (multiple windows or multiple workspaces), shows a single
/// quit confirmation dialog instead of doing it per-window.
pub fn toggleFullscreen(self: *WindowState) void {
const win: *c.GtkWindow = @ptrCast(self.gtk_window);
if (c.gtk_window_is_fullscreen(win) != 0) {
c.gtk_window_unfullscreen(win);
} else {
c.gtk_window_fullscreen(win);
}
}

pub fn quitApp(self: *WindowState) void {
const cfg = config_mod.get();
if (!cfg.confirm_close_window or !hasInterestingState(self.window_manager)) {
Expand Down Expand Up @@ -1552,6 +1561,16 @@ pub fn create(wm: *WindowManager) !*WindowState {
0,
);

// Hide CSD title bar when entering fullscreen
_ = c.g_signal_connect_data(
@as(c.gpointer, @ptrCast(window)),
"notify::fullscreened",
@as(c.GCallback, @ptrCast(&onFullscreenChanged)),
@ptrCast(state),
null,
0,
);

// Load theme-aware CSS
loadThemeCss();

Expand Down Expand Up @@ -1695,6 +1714,13 @@ fn onWindowFocusChanged(_: *c.GObject, _: ?*anyopaque, data: c.gpointer) callcon
}
}

fn onFullscreenChanged(_: *c.GObject, _: ?*anyopaque, data: c.gpointer) callconv(.c) void {
const state: *WindowState = @ptrCast(@alignCast(data));
const tv = state.toolbar_view orelse return;
const is_fullscreen = c.gtk_window_is_fullscreen(@as(*c.GtkWindow, @ptrCast(state.gtk_window)));
c.adw_toolbar_view_set_reveal_top_bars(@as(*c.AdwToolbarView, @ptrCast(tv)), if (is_fullscreen != 0) 0 else 1);
}

fn onWorkspaceSelect(index: usize) void {
if (window_manager) |wm| if (wm.active_window) |state| state.selectWorkspace(index);
}
Expand Down