summaryrefslogtreecommitdiff
path: root/main.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-06-30 20:29:43 -0300
committerGabriel Schneider <[email protected]>2026-06-30 22:21:44 -0300
commitf6089186cfe1517073fafc8979a7546ec773d6c1 (patch)
treecfb474b5a5cff5d7197aa5084ea6013a0c53df6f /main.zig
parent8b60c2c29c016c7db0fa6ee63769cea1defd9b3c (diff)
downloadpardes-full-prototype.tar.gz
pardes-full-prototype.zip
stealing petsciinatorfull-prototype
Diffstat (limited to 'main.zig')
-rw-r--r--main.zig46
1 files changed, 37 insertions, 9 deletions
diff --git a/main.zig b/main.zig
index 2ed94ccb..4c82d862 100644
--- a/main.zig
+++ b/main.zig
@@ -392,7 +392,12 @@ fn isDir(path: [*:0]const u8) bool {
// selectable text (acme-style). Caller frees with `alloc`.
fn tagLine(alloc: std.mem.Allocator, t: *Term) ![]u8 {
// an image pane's tag: a label + path (+ builtins); display-only, no mode/cwd.
- if (t.image) |iv| return std.fmt.allocPrint(alloc, "img {s} {s}", .{ iv.path, pane_builtins_str });
+ if (t.image) |iv| {
+ // toggles: Petscii (glyph art on/off), the color mode (C64 = commodore /
+ // Term = terminal palette), Ascii (include ascii glyphs in the matcher).
+ const mode = if (iv.pmode == .commodore) "C64" else "Term";
+ return std.fmt.allocPrint(alloc, "img {s} Petscii {s} Ascii {s}", .{ iv.path, mode, pane_builtins_str });
+ }
// a file pane's tag: mode + path (+ builtins); no cwd. Files never reach tty.
if (t.file) |f| {
const fmode = if (t.mode == .insert) "in" else "nm";
@@ -625,7 +630,7 @@ fn openFileView(gpa: std.mem.Allocator, rp: [*:0]const u8, line: usize, cols: u1
// sharing the layout machinery. No pty (handle -1), no reader, a stub 1x1 emulator
// (only its allocator-owned bits are touched). The render loop transmits + draws
// the image (see image.zig). `rp` is the absolute path. Caller wires it in + frees.
-fn openImageView(gpa: std.mem.Allocator, rp: [*:0]const u8, cols: u16, rows: u16) !*Term {
+fn openImageView(gpa: std.mem.Allocator, rp: [*:0]const u8, cols: u16, rows: u16, petscii: bool) !*Term {
const path = try gpa.dupe(u8, std.mem.span(rp));
errdefer gpa.free(path);
const t = try gpa.create(Term);
@@ -636,7 +641,7 @@ fn openImageView(gpa: std.mem.Allocator, rp: [*:0]const u8, cols: u16, rows: u16
.term = try ghostty_vt.Terminal.init(gpa, .{ .cols = 1, .rows = 1 }),
.stream = undefined,
.reader = .{ .any_future = null, .result = {} },
- .image = .{ .path = path },
+ .image = .{ .path = path, .petscii = petscii },
.cols = cols,
.rows = rows,
};
@@ -950,7 +955,7 @@ fn renderPane(root: vaxis.Window, alloc: std.mem.Allocator, t: *Term, r: Rect, a
// gutter so it reads like any other pane. kitty placements are re-issued each
// frame, so this runs every render. Nothing else (text/cursor/selection) applies.
if (t.image) |*iv| {
- if (r.h > BOX_H) image.draw(iv, text.child(.{ .y_off = BOX_H, .height = r.h - BOX_H }));
+ if (r.h > BOX_H) image.draw(iv, text.child(.{ .y_off = BOX_H, .height = r.h - BOX_H }), r.w - GUTTER, r.h - BOX_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;
@@ -2167,6 +2172,23 @@ fn actOnSelection(
txt: []const u8,
) bool {
const t = terms[id] orelse return false;
+ // image-pane tagline toggles (middle-click, acme execute): Petscii on/off, the
+ // C64/Term color mode, and Ascii glyphs. Intercept before the builtin/run dispatch.
+ if (button == .middle) if (t.image) |*iv| {
+ const w = std.mem.trim(u8, txt, " \t\r\n");
+ if (std.mem.eql(u8, w, "Petscii")) {
+ iv.petscii = !iv.petscii;
+ return false;
+ }
+ if (std.mem.eql(u8, w, "C64") or std.mem.eql(u8, w, "Term")) {
+ iv.pmode = if (iv.pmode == .commodore) .terminal else .commodore;
+ return false;
+ }
+ if (std.mem.eql(u8, w, "Ascii")) {
+ iv.ascii = !iv.ascii;
+ return false;
+ }
+ };
if (button == .right) {
// acme "look": resolve the selected word (peeling a :line suffix) relative
// to the window's dir. A directory opens/focuses a terminal there + ls; a
@@ -2214,9 +2236,9 @@ fn actOnSelection(
}
} else {
const fp = std.mem.span(rp);
- // an image opens as an IMAGE pane, but only when the terminal reported
- // the kitty-graphics capability; otherwise it falls back to a text view.
- const as_image = images_ok and image.isImagePath(fp);
+ // image paths open as IMAGE panes; they render via kitty when supported,
+ // else via the PETSCII fallback (also toggleable on the pane's tagline).
+ const as_image = image.isImagePath(fp);
// focus an existing pane already showing this path (file or image)
var found: ?usize = null;
for (terms, 0..) |*slot, i| if (slot.*) |tt| {
@@ -2238,7 +2260,7 @@ fn actOnSelection(
} else null;
if (slot) |free| {
const opened: ?*Term = if (as_image)
- (openImageView(gpa, rp, sw, sh) catch null)
+ (openImageView(gpa, rp, sw, sh, !images_ok) catch null) // petscii by default when no kitty
else
(openFileView(gpa, rp, pl.line, sw, sh) catch null);
if (opened) |nt| {
@@ -2882,7 +2904,13 @@ pub fn main(init: std.process.Init) !void {
if (slot.*) |t| {
// image panes transmit lazily (needs the tty writer, which lives
// here on the render thread); renderPane then re-places the picture.
- if (t.image) |*iv| image.ensureLoaded(iv, &vx, gpa, tty.writer());
+ if (t.image) |*iv| {
+ image.ensureLoaded(iv, &vx, gpa, tty.writer());
+ const r = rects[id];
+ const bc = if (r.w > GUTTER) r.w - GUTTER else 0;
+ const br = if (r.h > BOX_H) r.h - BOX_H else 0;
+ image.ensureGrid(iv, gpa, bc, br);
+ }
try renderPane(win, frame_arena.allocator(), t, rects[id], id == active);
}
}