Skip to content

Commit 79402e9

Browse files
v0.1 + v0.2 features: Unicode, cursor styles, bell, title, search, URLs
v0.1 must-haves: - Unicode fonts: 351 glyphs (ASCII + Latin-1 + box-drawing + block elements) - Window title: OSC 0/2 → XCB _NET_WM_NAME (x11 + wayland) - Cursor styles: block/underline/bar via DECSCUSR (ESC[N SP q) - Visual bell: BEL → 1-frame framebuffer RGB invert - Proportional pane resize: layout-aware per-pane dimensions v0.2 power-user: - Session restore: teru --attach loads saved session layout - URL detection: Ctrl+click opens URLs with xdg-open - Search: Ctrl+Space,/ highlights matches in visible grid - Status bar: workspace, pane info, dimensions at bottom - UrlDetector module with regex-free pattern matching 14,664 lines. 234 tests. All pass.
1 parent 2814fec commit 79402e9

12 files changed

Lines changed: 1204 additions & 62 deletions

File tree

src/core/Grid.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub const Cell = struct {
3434
}
3535
};
3636

37+
pub const CursorShape = enum { block, underline, bar };
38+
3739
cells: []Cell,
3840
rows: u16,
3941
cols: u16,
@@ -42,6 +44,8 @@ cursor_col: u16 = 0,
4244
scroll_top: u16 = 0,
4345
scroll_bottom: u16,
4446
dirty: bool = true,
47+
cursor_shape: CursorShape = .block,
48+
bell: bool = false,
4549

4650
/// Optional scrollback buffer. When set, lines that scroll off the top
4751
/// are captured as text and pushed to the scrollback.

src/core/KeyHandler.zig

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! into these helpers rather than inlining the logic.
66

77
const std = @import("std");
8+
const posix = std.posix;
89
const Io = std.Io;
910
const Multiplexer = @import("Multiplexer.zig");
1011
const ProcessGraph = @import("../graph/ProcessGraph.zig");
@@ -35,9 +36,22 @@ pub const PrefixState = struct {
3536
}
3637
};
3738

39+
// ── Mux command result ──────────────────────────────────────────
40+
41+
/// Action returned by handleMuxCommand for the caller to act on.
42+
pub const MuxAction = enum {
43+
none,
44+
enter_search,
45+
};
46+
3847
// ── Mux command dispatch ─────────────────────────────────────────
3948

49+
fn writeMsg(msg: []const u8) void {
50+
_ = std.c.write(posix.STDOUT_FILENO, msg.ptr, msg.len);
51+
}
52+
4053
/// Handle a multiplexer command after the prefix key (Ctrl+Space).
54+
/// Returns an action the caller should handle (e.g., entering search mode).
4155
pub fn handleMuxCommand(
4256
cmd: u8,
4357
mux: *Multiplexer,
@@ -47,11 +61,11 @@ pub fn handleMuxCommand(
4761
grid_rows: u16,
4862
grid_cols: u16,
4963
io: Io,
50-
) void {
64+
) MuxAction {
5165
switch (cmd) {
5266
'c' => {
5367
// Spawn new pane
54-
const id = mux.spawnPane(grid_rows, grid_cols) catch return;
68+
const id = mux.spawnPane(grid_rows, grid_cols) catch return .none;
5569
if (mux.getPaneById(id)) |pane| {
5670
// Graph registration failure is non-fatal: pane works without tracking
5771
_ = graph.spawn(.{ .name = "shell", .kind = .shell, .pid = pane.pty.child_pid }) catch {};
@@ -75,12 +89,23 @@ pub fn handleMuxCommand(
7589
' ' => mux.cycleLayout(),
7690
'd' => {
7791
// Detach: save session and exit
78-
mux.saveSession(graph, "/tmp/teru-session.bin", io) catch {
79-
// Session save failed — still detach (data loss over hang)
92+
const path = "/tmp/teru-session.bin";
93+
const pane_n = mux.panes.items.len;
94+
mux.saveSession(graph, path, io) catch {
95+
writeMsg("[teru] Session save failed, exiting anyway\n");
96+
running.* = false;
97+
return .none;
8098
};
8199
hooks.fire(.session_save);
100+
var dbuf: [256]u8 = undefined;
101+
const dmsg = std.fmt.bufPrint(&dbuf, "[teru] Session saved to {s} ({d} panes)\n[teru] Note: processes are not preserved. Use --attach to restore layout.\n", .{ path, pane_n }) catch "[teru] Session saved\n";
102+
writeMsg(dmsg);
82103
running.* = false;
83104
},
105+
'/' => {
106+
// Enter search mode (caller handles the UI)
107+
return .enter_search;
108+
},
84109
'1'...'9' => {
85110
// Switch workspace (1-based → 0-based)
86111
mux.switchWorkspace(cmd - '1');
@@ -95,6 +120,7 @@ pub fn handleMuxCommand(
95120
}
96121
},
97122
}
123+
return .none;
98124
}
99125

100126
// ── Tests ────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)