Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261const 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);}