summaryrefslogtreecommitdiff
path: root/replay.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-03 14:43:30 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitd1ed5bd8f55935e6470c4a7559216119571a084f (patch)
tree434478ce8a5dcd9aed367334816f54da9953e56d /replay.zig
parent153f0a88862507cf1f5dda71f71db3d6649dfeaa (diff)
downloadpardes-d1ed5bd8f55935e6470c4a7559216119571a084f.tar.gz
pardes-d1ed5bd8f55935e6470c4a7559216119571a084f.zip
fixed scaling and fitting on web ui
Diffstat (limited to 'replay.zig')
-rw-r--r--replay.zig246
1 files changed, 55 insertions, 191 deletions
diff --git a/replay.zig b/replay.zig
index 18deab13..6cec683e 100644
--- a/replay.zig
+++ b/replay.zig
@@ -6,6 +6,7 @@ const replay_config = @import("replay_config");
const dump = @import("dump.zig");
const paint = @import("paint.zig");
const image = @import("image.zig");
+const pane_ui = @import("pane_ui.zig");
const is_emscripten = builtin.os.tag == .emscripten;
const embedded_dump = if (replay_config.embedded_dump) @embedFile("replay.dump.zon") else "";
@@ -25,14 +26,14 @@ pub const std_options_debug_io: std.Io = if (is_emscripten)
else
std_options_debug_threaded_io.?.io();
-const TOPBAR_H: u16 = 1;
-const GUTTER: u16 = 2;
-const BOX_H: u16 = 1;
-const MINW: u16 = 10;
-const MINH: u16 = 3;
-const PREFIX_W: u16 = 5;
+const TOPBAR_H: u16 = pane_ui.TOPBAR_H;
+const GUTTER: u16 = pane_ui.GUTTER;
+const BOX_H: u16 = pane_ui.BOX_H;
+const MINW: u16 = pane_ui.MINW;
+const MINH: u16 = pane_ui.MINH;
+const PREFIX_W: u16 = pane_ui.PREFIX_W;
-const Rect = struct { x: u16, y: u16, w: u16, h: u16 };
+const Rect = pane_ui.Rect;
const ReplayTerm = struct {
term: ghostty_vt.Terminal,
@@ -60,12 +61,8 @@ const Pane = struct {
}
};
-const Layout = struct {
- ncol: usize,
- col_weight: [dump.max_cols]f32,
- col_terms: [dump.max_cols][dump.max_panes]usize,
- col_n: [dump.max_cols]usize,
-};
+const Layout = pane_ui.Layout(dump.max_cols, dump.max_panes);
+const moveTerm = pane_ui.moveTerm;
const Drag = union(enum) {
none,
@@ -164,115 +161,8 @@ fn terminalBodyText(alloc: std.mem.Allocator, p: *Pane) ![]const u8 {
return out.toOwnedSlice(alloc);
}
-fn layoutFindTerm(l: *const Layout, id: usize) ?struct { col: usize, idx: usize } {
- var c: usize = 0;
- while (c < l.ncol) : (c += 1) {
- var k: usize = 0;
- while (k < l.col_n[c]) : (k += 1) {
- if (l.col_terms[c][k] == id) return .{ .col = c, .idx = k };
- }
- }
- return null;
-}
-
-fn layoutRemove(l: *Layout, id: usize) void {
- const f = layoutFindTerm(l, id) orelse return;
- const c = f.col;
- var k = f.idx;
- while (k + 1 < l.col_n[c]) : (k += 1) l.col_terms[c][k] = l.col_terms[c][k + 1];
- l.col_n[c] -= 1;
- if (l.col_n[c] == 0 and l.ncol > 1) {
- l.col_weight[if (c > 0) c - 1 else c + 1] += l.col_weight[c];
- var j = c;
- while (j + 1 < l.ncol) : (j += 1) {
- l.col_terms[j] = l.col_terms[j + 1];
- l.col_n[j] = l.col_n[j + 1];
- l.col_weight[j] = l.col_weight[j + 1];
- }
- l.ncol -= 1;
- }
-}
-
-fn layoutInsert(l: *Layout, c: usize, idx: usize, id: usize) void {
- var k = l.col_n[c];
- while (k > idx) : (k -= 1) l.col_terms[c][k] = l.col_terms[c][k - 1];
- l.col_terms[c][idx] = id;
- l.col_n[c] += 1;
-}
-
-fn computeGeom(
- l: *const Layout,
- panes: []const Pane,
- sw: u16,
- sh: u16,
- rects: *[dump.max_panes]Rect,
- col_x: *[dump.max_cols]u16,
- col_w: *[dump.max_cols]u16,
-) void {
- if (l.ncol == 0) return;
- var wsum: f32 = 0;
- for (0..l.ncol) |c| wsum += l.col_weight[c];
- if (wsum <= 0) wsum = 1;
-
- var x: u16 = 0;
- for (0..l.ncol) |c| {
- const last = c + 1 == l.ncol;
- const cw: u16 = if (last) (sw -| x) else @max(1, @as(u16, @intFromFloat(@round(@as(f32, @floatFromInt(sw)) * l.col_weight[c] / wsum))));
- col_x[c] = x;
- col_w[c] = cw;
-
- var vsum: f32 = 0;
- for (0..l.col_n[c]) |k| vsum += panes[l.col_terms[c][k]].vweight;
- if (vsum <= 0) vsum = 1;
-
- var y: u16 = TOPBAR_H;
- const avail_h = sh -| TOPBAR_H;
- for (0..l.col_n[c]) |k| {
- const id = l.col_terms[c][k];
- const lastk = k + 1 == l.col_n[c];
- const ch: u16 = if (lastk) (sh -| y) else @max(1, @as(u16, @intFromFloat(@round(@as(f32, @floatFromInt(avail_h)) * panes[id].vweight / vsum))));
- rects[id] = .{ .x = x, .y = y, .w = cw, .h = ch };
- y +|= ch;
- }
- x +|= cw;
- }
-}
-
-fn moveTerm(
- l: *Layout,
- rects: *const [dump.max_panes]Rect,
- col_x: *const [dump.max_cols]u16,
- col_w: *const [dump.max_cols]u16,
- id: usize,
- cur_x: u16,
- cur_y: u16,
-) void {
- const src = layoutFindTerm(l, id) orelse return;
- var tc: usize = if (l.ncol > 0) l.ncol - 1 else 0;
- for (0..l.ncol) |c| {
- if (cur_x >= col_x[c] and cur_x < col_x[c] + col_w[c]) {
- tc = c;
- break;
- }
- }
-
- var ti: usize = 0;
- var k: usize = 0;
- while (k < l.col_n[tc]) : (k += 1) {
- const cid = l.col_terms[tc][k];
- if (cid == id) continue;
- const r = rects[cid];
- if (cur_y < r.y + r.h / 2) break;
- ti += 1;
- }
-
- const src_solo = l.col_n[src.col] == 1;
- if (tc == src.col and src_solo) return;
- layoutRemove(l, id);
- if (src_solo and src.col < tc) tc -= 1;
- if (tc >= l.ncol) tc = l.ncol - 1;
- if (ti > l.col_n[tc]) ti = l.col_n[tc];
- layoutInsert(l, tc, ti, id);
+fn paneVWeight(panes: []const Pane, id: usize) ?f32 {
+ return panes[id].vweight;
}
fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, active: bool) !void {
@@ -291,16 +181,7 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti
if (p.image_view) |*iv| {
if (body_h > 0) image.draw(iv, text.child(.{ .y_off = BOX_H, .height = body_h }), body_w, body_h);
- const gut = root.child(.{ .x_off = r.x, .y_off = r.y, .width = GUTTER, .height = r.h });
- const box_bg = if (active) accent else accent_dim;
- var gy: u16 = 0;
- while (gy < r.h) : (gy += 1) {
- const bg = if (gy < BOX_H) box_bg else T().scroll_track;
- var gx: u16 = 0;
- while (gx < GUTTER) : (gx += 1) {
- gut.writeCell(gx, gy, .{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(bg) } });
- }
- }
+ pane_ui.drawGutter(root, r, active, accent, accent_dim, T().scroll_track, T().scroll_thumb, null);
return;
}
@@ -381,44 +262,15 @@ fn renderPane(alloc: std.mem.Allocator, root: ui.Window, p: *Pane, r: Rect, acti
}
}
- const gut = root.child(.{ .x_off = r.x, .y_off = r.y, .width = GUTTER, .height = r.h });
- const box_bg = if (active) accent else accent_dim;
- var gy: u16 = 0;
- while (gy < r.h) : (gy += 1) {
- const bg = if (gy < BOX_H) box_bg else T().scroll_track;
- var gx: u16 = 0;
- while (gx < GUTTER) : (gx += 1) {
- gut.writeCell(gx, gy, .{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(bg) } });
- }
- }
-
- const track_h: usize = if (r.h > BOX_H) r.h - BOX_H else 0;
- if (track_h > 0) {
- var sb_total: usize = undefined;
- var sb_off: usize = undefined;
- var sb_len: usize = undefined;
- if (p.terminal) |*rt| {
- const sb = rt.term.screens.active.pages.scrollbar();
- sb_total = sb.total;
- sb_off = sb.offset;
- sb_len = sb.len;
- } else {
- sb_total = lineCount(p.content);
- sb_off = p.scroll;
- sb_len = p.rows;
- }
- const total = if (sb_total == 0) 1 else sb_total;
- const len = @max(1, (track_h * @max(1, sb_len)) / total);
- const pos = (track_h * sb_off) / total;
- var sy: usize = 0;
- while (sy < track_h) : (sy += 1) {
- if (sy < pos or sy >= pos + len) continue;
- var gx: u16 = 0;
- while (gx < GUTTER) : (gx += 1) {
- gut.writeCell(gx, @intCast(BOX_H + sy), .{ .char = .{ .grapheme = " " }, .style = .{ .bg = rgbColor(T().scroll_thumb) } });
- }
- }
- }
+ const scrollbar: pane_ui.Scrollbar = if (p.terminal) |*rt| blk: {
+ const sb = rt.term.screens.active.pages.scrollbar();
+ break :blk .{ .total = sb.total, .offset = sb.offset, .len = sb.len };
+ } else .{
+ .total = lineCount(p.content),
+ .offset = p.scroll,
+ .len = p.rows,
+ };
+ pane_ui.drawGutter(root, r, active, accent, accent_dim, T().scroll_track, T().scroll_thumb, scrollbar);
}
fn render(
@@ -428,8 +280,13 @@ fn render(
state: dump.State,
panes: []Pane,
rects: *[dump.max_panes]Rect,
+ layout: *const Layout,
+ col_x: *const [dump.max_cols]u16,
+ col_w: *const [dump.max_cols]u16,
screen_w: u16,
+ screen_h: u16,
active: usize,
+ drag: Drag,
) !void {
var arena: std.heap.ArenaAllocator = .init(gpa);
defer arena.deinit();
@@ -452,15 +309,14 @@ fn render(
const topbar = if (state.topbar.len > 0) state.topbar else "pardes replay";
_ = bar.printSegment(.{ .text = topbar, .style = .{ .fg = rgbColor(T().tag_fg), .bg = rgbColor(T().tag_bg) } }, .{ .wrap = .none });
- try vx.render(writer);
-}
-
-fn paneAt(rects: *const [dump.max_panes]Rect, pane_count: usize, col: u16, row: u16) ?usize {
- for (0..pane_count) |i| {
- const r = rects[i];
- if (col >= r.x and col < r.x + r.w and row >= r.y and row < r.y + r.h) return i;
+ switch (drag) {
+ .border_v => |d| pane_ui.drawBorderV(win, d.cur_x, screen_h),
+ .border_h => |d| pane_ui.drawBorderH(win, col_x[d.col], col_w[d.col], d.cur_y),
+ .move => |d| pane_ui.drawMovePreview(win, layout, col_x, col_w, d.cur_x, d.cur_y, T().scroll_thumb),
+ .none => {},
}
- return null;
+
+ try vx.render(writer);
}
fn deinitPanes(gpa: std.mem.Allocator, panes: []Pane) void {
@@ -626,7 +482,7 @@ const App = struct {
try self.loop.start();
try self.loop.installResizeHandler();
- computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w);
+ pane_ui.computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w, paneVWeight);
}
fn deinit(self: *App) void {
@@ -655,16 +511,14 @@ const App = struct {
fn step(self: *App, event: @TypeOf(Command.value)) !bool {
switch (event) {
.winsize => |ws| {
- if (!is_emscripten) {
- self.screen_w = ws.cols;
- self.screen_h = ws.rows;
- try self.vx.resize(self.gpa, self.tty.writer(), ws);
- }
+ self.screen_w = ws.cols;
+ self.screen_h = ws.rows;
+ try self.vx.resize(self.gpa, self.tty.writer(), ws);
},
.mouse => |mouse| {
const mcol: u16 = if (mouse.col < 0) 0 else @min(@as(u16, @intCast(mouse.col)), self.screen_w -| 1);
const mrow: u16 = if (mouse.row < 0) 0 else @min(@as(u16, @intCast(mouse.row)), self.screen_h -| 1);
- const hovered = paneAt(&self.rects, self.panes.len, mcol, mrow);
+ const hovered = pane_ui.paneAt(&self.rects, self.panes.len, mcol, mrow);
if (mouse.button == .wheel_down and mouse.type == .press) {
if (hovered) |id| scrollBy(&self.panes[id], 1);
@@ -698,13 +552,23 @@ const App = struct {
if (!handled) if (hovered) |id| {
self.active = id;
const r = self.rects[id];
- if (mcol < r.x + GUTTER and mrow < r.y + BOX_H) {
+ if (pane_ui.inMoveBox(r, mcol, mrow)) {
self.drag = .{ .move = .{ .id = id, .cur_x = mcol, .cur_y = mrow } };
- } else if (mcol < r.x + GUTTER) {
- const local: i32 = @as(i32, mrow) - @as(i32, r.y + BOX_H);
- scrollBy(&self.panes[id], -local);
+ } else if (pane_ui.scrollbarClickDelta(r, .left, mcol, mrow)) |delta| {
+ scrollBy(&self.panes[id], delta);
}
};
+ } else if (mouse.type == .press and (mouse.button == .middle or mouse.button == .right)) {
+ if (hovered) |id| {
+ const r = self.rects[id];
+ const sb_button: pane_ui.ScrollbarButton = if (mouse.button == .right) .right else .middle;
+ if (pane_ui.scrollbarClickDelta(r, sb_button, mcol, mrow)) |delta| {
+ if (mouse.button == .right) self.active = id;
+ scrollBy(&self.panes[id], delta);
+ } else {
+ self.active = id;
+ }
+ }
} else if (mouse.type == .press) {
if (hovered) |id| self.active = id;
} else if (mouse.type == .drag) {
@@ -775,7 +639,7 @@ const App = struct {
else => {},
}
- computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w);
+ pane_ui.computeGeom(&self.layout, self.panes, self.screen_w, self.screen_h, &self.rects, &self.col_x, &self.col_w, paneVWeight);
for (self.panes, 0..) |*p, id| {
const r = self.rects[id];
const cols = @max(1, r.w -| GUTTER);
@@ -787,7 +651,7 @@ const App = struct {
p.rows = rows;
clampScroll(p);
}
- try render(&self.vx, self.gpa, self.tty.writer(), self.state, self.panes, &self.rects, self.screen_w, self.active);
+ try render(&self.vx, self.gpa, self.tty.writer(), self.state, self.panes, &self.rects, &self.layout, &self.col_x, &self.col_w, self.screen_w, self.screen_h, self.active, self.drag);
return true;
}
};