const std = @import("std"); const Io = std.Io; // ── Color Constants ────────────────────────────────────────────────────────── pub const Color = struct { pub const reset = "\x1b[0m"; pub const bold = "\x1b[1m"; pub const dim = "\x1b[2m"; pub const italic = "\x1b[3m"; pub const underline = "\x1b[4m"; // Foreground pub const red = "\x1b[31m"; pub const green = "\x1b[32m"; pub const yellow = "\x1b[33m"; pub const blue = "\x1b[34m"; pub const magenta = "\x1b[35m"; pub const cyan = "\x1b[36m"; pub const white = "\x1b[37m"; pub const gray = "\x1b[90m"; // Bold foreground pub const bold_red = "\x1b[1;31m"; pub const bold_green = "\x1b[1;32m"; pub const bold_yellow = "\x1b[1;33m"; pub const bold_blue = "\x1b[1;34m"; pub const bold_magenta = "\x1b[1;35m"; pub const bold_cyan = "\x1b[1;36m"; pub const bold_white = "\x1b[1;37m"; }; // ── Icons ──────────────────────────────────────────────────────────────────── pub const Icon = struct { pub const check = "✓"; pub const cross = "✗"; pub const bullet = "●"; pub const circle = "○"; pub const active = "◉"; pub const arrow = "→"; pub const warn = "!"; pub const lock = "🔒"; pub const available = "✅"; pub const star = "★"; }; // ── Color Detection ────────────────────────────────────────────────────────── pub fn shouldColor() bool { // Check NO_COLOR env var (https://no-color.org/) if (std.c.getenv("NO_COLOR")) |_| return false; // Note: --color=never flag is checked via args in each CLI command // since std.process.args() is not available in Zig 0.17 return true; } pub fn shouldColorFromArgs(args: []const []const u8) bool { if (std.c.getenv("NO_COLOR")) |_| return false; for (args) |arg| { if (std.mem.eql(u8, arg, "--color=never")) return false; } return true; } fn c(color: []const u8) []const u8 { return if (shouldColor()) color else ""; } // ── Stderr Output ──────────────────────────────────────────────────────────── pub fn err(io: Io, msg: []const u8) !void { const w = Io.File.stderr(); try w.writeStreamingAll(io, c(Color.red)); try w.writeStreamingAll(io, "error: "); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, msg); try w.writeStreamingAll(io, "\n"); } pub fn errWithHint(io: Io, msg: []const u8, hint: []const u8) !void { const w = Io.File.stderr(); try w.writeStreamingAll(io, c(Color.red)); try w.writeStreamingAll(io, "error: "); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, msg); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, c(Color.dim)); try w.writeStreamingAll(io, " hint: "); try w.writeStreamingAll(io, hint); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, "\n"); } pub fn warn(io: Io, msg: []const u8) !void { const w = Io.File.stderr(); try w.writeStreamingAll(io, c(Color.yellow)); try w.writeStreamingAll(io, "warn: "); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, msg); try w.writeStreamingAll(io, "\n"); } // ── Success Output ─────────────────────────────────────────────────────────── pub fn success(io: Io, msg: []const u8) !void { const w = Io.File.stdout(); try w.writeStreamingAll(io, c(Color.green)); try w.writeStreamingAll(io, Icon.check); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, " "); try w.writeStreamingAll(io, msg); try w.writeStreamingAll(io, "\n"); } pub fn successDim(io: Io, msg: []const u8, dim_text: []const u8) !void { const w = Io.File.stdout(); try w.writeStreamingAll(io, c(Color.green)); try w.writeStreamingAll(io, Icon.check); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, " "); try w.writeStreamingAll(io, msg); try w.writeStreamingAll(io, c(Color.dim)); try w.writeStreamingAll(io, dim_text); try w.writeStreamingAll(io, c(Color.reset)); try w.writeStreamingAll(io, "\n"); } // ── Formatting Helpers ─────────────────────────────────────────────────────── pub fn writeStyled(writer: anytype, io: Io, label: []const u8, value: []const u8, color: []const u8) !void { try writer.writeStreamingAll(io, c(Color.dim)); try writer.writeStreamingAll(io, label); try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, " "); try writer.writeStreamingAll(io, c(color)); try writer.writeStreamingAll(io, value); try writer.writeStreamingAll(io, c(Color.reset)); } pub fn writeHeader(writer: anytype, io: Io, title: []const u8) !void { try writer.writeStreamingAll(io, c(Color.bold)); try writer.writeStreamingAll(io, title); try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, "\n"); } pub fn writeSection(writer: anytype, io: Io, title: []const u8) !void { try writer.writeStreamingAll(io, "\n"); try writer.writeStreamingAll(io, c(Color.bold)); try writer.writeStreamingAll(io, title); try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, "\n"); } pub fn writeStatusLine(writer: anytype, io: Io, label: []const u8, value: []const u8, color: []const u8) !void { try writer.writeStreamingAll(io, " "); try writer.writeStreamingAll(io, c(Color.dim)); try writer.writeStreamingAll(io, label); try writer.writeStreamingAll(io, c(Color.reset)); if (label.len < 14) { const pad = 14 - label.len; var i: usize = 0; while (i < pad) : (i += 1) { try writer.writeStreamingAll(io, " "); } } try writer.writeStreamingAll(io, c(color)); try writer.writeStreamingAll(io, value); try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, "\n"); } pub fn writeTableHeader(writer: anytype, io: Io, columns: []const []const u8, widths: []const usize) !void { try writer.writeStreamingAll(io, c(Color.dim)); for (columns, 0..) |col, i| { try writer.writeStreamingAll(io, col); const pad = widths[i] - col.len; var j: usize = 0; while (j < pad) : (j += 1) { try writer.writeStreamingAll(io, " "); } if (i < columns.len - 1) try writer.writeStreamingAll(io, " "); } try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, "\n"); try writer.writeStreamingAll(io, c(Color.dim)); for (widths) |w| { var j: usize = 0; while (j < w) : (j += 1) { try writer.writeStreamingAll(io, "-"); } try writer.writeStreamingAll(io, " "); } try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, "\n"); } pub fn writeTableRow(writer: anytype, io: Io, values: []const []const u8, widths: []const usize, colors: []const []const u8) !void { for (values, 0..) |val, i| { const color = if (i < colors.len) colors[i] else Color.white; try writer.writeStreamingAll(io, c(color)); try writer.writeStreamingAll(io, val); try writer.writeStreamingAll(io, c(Color.reset)); const pad = widths[i] - val.len; var j: usize = 0; while (j < pad) : (j += 1) { try writer.writeStreamingAll(io, " "); } if (i < values.len - 1) try writer.writeStreamingAll(io, " "); } try writer.writeStreamingAll(io, "\n"); } pub fn writeBadge(writer: anytype, io: Io, text: []const u8, bg_color: []const u8) !void { try writer.writeStreamingAll(io, c(bg_color)); try writer.writeStreamingAll(io, c(Color.bold)); try writer.writeStreamingAll(io, " "); try writer.writeStreamingAll(io, text); try writer.writeStreamingAll(io, " "); try writer.writeStreamingAll(io, c(Color.reset)); } pub fn writeKeyValue(writer: anytype, io: Io, key: []const u8, value: []const u8, key_color: []const u8, value_color: []const u8) !void { try writer.writeStreamingAll(io, c(key_color)); try writer.writeStreamingAll(io, key); try writer.writeStreamingAll(io, c(Color.reset)); try writer.writeStreamingAll(io, ": "); try writer.writeStreamingAll(io, c(value_color)); try writer.writeStreamingAll(io, value); try writer.writeStreamingAll(io, c(Color.reset)); } // ── Convenience Wrappers ───────────────────────────────────────────────────── pub fn boldText(writer: anytype, io: Io, text: []const u8) !void { try writer.writeStreamingAll(io, c(Color.bold)); try writer.writeStreamingAll(io, text); try writer.writeStreamingAll(io, c(Color.reset)); } pub fn dimText(writer: anytype, io: Io, text: []const u8) !void { try writer.writeStreamingAll(io, c(Color.dim)); try writer.writeStreamingAll(io, text); try writer.writeStreamingAll(io, c(Color.reset)); } pub fn coloredText(writer: anytype, io: Io, text: []const u8, color: []const u8) !void { try writer.writeStreamingAll(io, c(color)); try writer.writeStreamingAll(io, text); try writer.writeStreamingAll(io, c(Color.reset)); } test "color constants" { try std.testing.expectEqualStrings("\x1b[0m", Color.reset); try std.testing.expectEqualStrings("\x1b[1m", Color.bold); try std.testing.expectEqualStrings("\x1b[31m", Color.red); }