File size: 4,822 Bytes
20f108c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | // Phantom β shared visual components
// ANSI colors, ASCII art, banners, prompts, separators
// Neon violet/cyan on dark β hacker-in-hoodie aesthetic
// ββ ANSI helpers (local β no dependency on phantom.mjs) ββ
function fg(name) {
const colors = { black:30, red:31, green:32, yellow:33, blue:34, magenta:35, cyan:36, white:37, dim:2 };
const n = colors[name] ?? 37;
return `\x1b[${n}m`;
}
const _R = "\x1b[0m";
const _B = "\x1b[1m";
const _D = "\x1b[2m";
// ββ Hooded figure logo (cyberpunk neon) βββββββββββββββββ
export function renderLogo(opts = {}) {
const {
mode = "", // "non-interactive", "v0.2.0"
tools = 0,
wide = false,
} = opts;
const M = fg("magenta");
const C = fg("cyan");
const G = fg("green");
const Y = fg("yellow");
const dim = fg("dim");
const _r = _R;
const b = _B;
const d = _D;
// Shadow figlet logo β no box-drawing borders, clean
const lines = [
` ${M}${b} _ \\ | | \\ \\ |__ __| _ \\ \\ |${_r}`,
` ${M}${b} | | | | _ \\ \\ | | | | |\\/ |${_r}`,
` ${M}${b} ___/ ___ | ___ \\ |\\ | | | | | |${_r}`,
` ${M}${b} _| _| _|_/ _\\_| \\_| _| \\___/ _| _|${_r}`,
` ${C}${b}P${_r} ${M}${b}H${_r} ${Y}${b}A${_r} ${C}${b}N${_r} ${M}${b}T${_r} ${Y}${b}O${_r} ${C}${b}M${_r}`,
];
if (tools) lines.push(` ${dim}${tools} tools${_r}`);
return lines.join("\n");
}
// ββ Simple banner (small terminals / DesktopUI) ββ
export function renderBanner(text = "PHANTOM", subtitle = "") {
const C = fg("cyan");
const dim = fg("dim");
const _r = _R;
const b = _B;
return `${C}${b}${text}${_r}${subtitle ? ` ${dim}${subtitle}${_r}` : ""}`;
}
// ββ Prompt symbols ββββββββββββββββββββββββββββββββ
export const prompt = {
ghost: `${fg("green")}π»${_R} `,
bolt: `${fg("cyan")}β‘${_R} `,
hash: `${fg("magenta")}#${_R} `,
arrow: `${fg("green")}β${_R} `,
dim: `${fg("dim")}β§©${_R} `,
};
// ββ Separator line ββββββββββββββββββββββββββββββββββ
export function separator(char = "β", len = 50) {
return `${fg("dim")}${char.repeat(len)}${_R}`;
}
// ββ Status icons ββββββββββββββββββββββββββββββββββββ
// ββ Animated spinner ββββββββββββββββββββββββββββββββββ
// Minimal frame-based spinner; no external deps.
export function createSpinner(startTime, opts = {}) {
const frames = ['β ', 'β ', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ', 'β '];
let i = 0, timer = null;
const write = opts.write || ((s) => process.stdout.write(s));
const fmt = (ts) => {
const s = Math.floor((Date.now() - ts) / 1000);
return s > 3600 ? `${Math.floor(s/3600)}h ${Math.floor((s%3600)/60)}m`
: s > 60 ? `${Math.floor(s/60)}m ${s%60}s`
: `${s}s`;
};
return {
start(msg) {
if (timer) return;
timer = setInterval(() => {
const f = frames[i = (i + 1) % frames.length];
write(`\r${fg("cyan")}${_B}${f}${_R} [${fmt(startTime)}] ${msg}`);
}, 80);
},
stop() {
if (!timer) return;
clearInterval(timer); timer = null;
write('\r\x1b[K');
},
update(msg) {
const f = timer ? frames[i] : frames[0];
write(`\r${fg("cyan")}${_B}${f}${_R} [${fmt(startTime)}] ${msg}`);
}
};
}
export const icons = {
ok: `${fg("green")}β${_R}`,
err: `${fg("red")}β${_R}`,
warn: `${fg("yellow")}β ${_R}`,
info: `${fg("cyan")}β${_R}`,
bullet: `${fg("magenta")}β${_R}`,
arrow: `${fg("green")}β${_R}`,
};
// ββ Chat border β wraps multi-line text in a bordered box ββ
// width = total width (defaults to terminal cols - 2)
// title = optional top-left label
// color = border color function (default dim)
export function chatBorder(text, opts = {}) {
const { width, title, color = fg("dim") } = opts;
const cols = width || (process.stdout.columns || 80) - 4;
const c = color;
const _r = _R;
const lines = text.split("\n");
const out = [];
const top = title
? `${c}ββ ${_B}${title}${_R}${c} ${"β".repeat(Math.max(0, cols - title.length - 4))}β${_r}`
: `${c}β${"β".repeat(cols + 2)}β${_r}`;
out.push(top);
for (const line of lines) {
const wrapped = line.length > cols ? line.substring(0, cols - 1) + "β¦" : line;
const pad = " ".repeat(Math.max(0, cols - wrapped.length + 2));
out.push(`${c}β${_r} ${wrapped}${pad}${c}β${_r}`);
}
out.push(`${c}β${"β".repeat(cols + 2)}β${_r}`);
return out.join("\n");
}
|