diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fae9fa4c5..2136c80ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -95,7 +95,15 @@ jobs: fail-fast: false matrix: dir: - [c-vt, c-vt-key-encode, c-vt-paste, c-vt-sgr, zig-vt, zig-vt-stream] + [ + c-vt, + c-vt-key-encode, + c-vt-paste, + c-vt-sgr, + zig-formatter, + zig-vt, + zig-vt-stream, + ] name: Example ${{ matrix.dir }} runs-on: namespace-profile-ghostty-sm needs: test diff --git a/example/zig-formatter/README.md b/example/zig-formatter/README.md new file mode 100644 index 000000000..777fa5d7f --- /dev/null +++ b/example/zig-formatter/README.md @@ -0,0 +1,24 @@ +# Example: stdin to HTML using `vtStream` and `TerminalFormatter` + +This example demonstrates how to read VT sequences from stdin, parse them +using `vtStream`, and output styled HTML using `TerminalFormatter`. The +purpose of this example is primarily to show how to use formatters with +terminals. + +Requires the Zig version stated in the `build.zig.zon` file. + +## Usage + +Basic usage: + +```shell-session +echo -e "Hello \033[1;32mGreen\033[0m World" | zig build run +``` + +This will output HTML with inline styles and CSS palette variables. + +You can also pipe complex terminal output: + +```shell-session +ls --color=always | zig build run > output.html +``` diff --git a/example/zig-formatter/build.zig b/example/zig-formatter/build.zig new file mode 100644 index 000000000..54cdb3ee0 --- /dev/null +++ b/example/zig-formatter/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const run_step = b.step("run", "Run the app"); + const test_step = b.step("test", "Run unit tests"); + + const exe_mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + if (b.lazyDependency("ghostty", .{})) |dep| { + exe_mod.addImport( + "ghostty-vt", + dep.module("ghostty-vt"), + ); + } + + const exe = b.addExecutable(.{ + .name = "zig_formatter", + .root_module = exe_mod, + }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd.addArgs(args); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_module = exe_mod, + }); + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/example/zig-formatter/build.zig.zon b/example/zig-formatter/build.zig.zon new file mode 100644 index 000000000..9388a248f --- /dev/null +++ b/example/zig-formatter/build.zig.zon @@ -0,0 +1,13 @@ +.{ + .name = .zig_formatter, + .version = "0.0.0", + .fingerprint = 0x578de530797eafe6, + .dependencies = .{ + .ghostty = .{ .path = "../../" }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/example/zig-formatter/src/main.zig b/example/zig-formatter/src/main.zig new file mode 100644 index 000000000..87a8e4915 --- /dev/null +++ b/example/zig-formatter/src/main.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const ghostty_vt = @import("ghostty-vt"); + +pub fn main() !void { + var gpa: std.heap.DebugAllocator(.{}) = .init; + defer _ = gpa.deinit(); + const alloc = gpa.allocator(); + + // Create a terminal + var t: ghostty_vt.Terminal = try .init(alloc, .{ .cols = 150, .rows = 80 }); + defer t.deinit(alloc); + + // Create a read-only VT stream for parsing terminal sequences + var stream = t.vtStream(); + defer stream.deinit(); + + // Read from stdin + const stdin = std.fs.File.stdin(); + var buf: [4096]u8 = undefined; + while (true) { + const n = try stdin.readAll(&buf); + if (n == 0) break; + + // Replace \n with \r\n + for (buf[0..n]) |byte| { + if (byte == '\n') try stream.next('\r'); + try stream.next(byte); + } + } + + // Use TerminalFormatter to emit HTML + const formatter: ghostty_vt.formatter.TerminalFormatter = .init(&t, .{ + .emit = .html, + .palette = &t.color_palette.colors, + }); + + // Write to stdout + var stdout_writer = std.fs.File.stdout().writer(&buf); + const stdout = &stdout_writer.interface; + try stdout.print("{f}", .{formatter}); +}