termstyle-plus
Version:
The most comprehensive and powerful terminal styling library with extensive color support, gradients, animations, and special effects - TypeScript Edition
260 lines (224 loc) • 8.38 kB
text/typescript
import { RGB, Colors, Styles, BoxStyle, NeonColor, Brighten as BrightenInterface } from './types';
class Brighten implements BrightenInterface {
public readonly colors: Colors;
public readonly bg: Colors;
public readonly styles: Styles;
[key: string]: any;
constructor() {
// Basic colors
this.colors = {
// Standard colors
black: '\x1b[30m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
// Bright colors
brightBlack: '\x1b[90m',
brightRed: '\x1b[91m',
brightGreen: '\x1b[92m',
brightYellow: '\x1b[93m',
brightBlue: '\x1b[94m',
brightMagenta: '\x1b[95m',
brightCyan: '\x1b[96m',
brightWhite: '\x1b[97m',
// Extended colors
orange: '\x1b[38;2;255;165;0m',
pink: '\x1b[38;2;255;192;203m',
purple: '\x1b[38;2;128;0;128m',
brown: '\x1b[38;2;165;42;42m',
gold: '\x1b[38;2;255;215;0m',
silver: '\x1b[38;2;192;192;192m',
lime: '\x1b[38;2;0;255;0m',
teal: '\x1b[38;2;0;128;128m',
indigo: '\x1b[38;2;75;0;130m',
violet: '\x1b[38;2;238;130;238m',
// Precious stones
emerald: '\x1b[38;2;46;204;113m',
ruby: '\x1b[38;2;224;17;95m',
sapphire: '\x1b[38;2;15;82;186m',
amethyst: '\x1b[38;2;153;102;204m',
jade: '\x1b[38;2;0;168;107m',
amber: '\x1b[38;2;255;191;0m',
topaz: '\x1b[38;2;255;200;124m',
pearl: '\x1b[38;2;240;234;214m',
opal: '\x1b[38;2;177;231;236m',
garnet: '\x1b[38;2;179;27;27m',
// Nature colors
forest: '\x1b[38;2;34;139;34m',
sky: '\x1b[38;2;135;206;235m',
ocean: '\x1b[38;2;0;119;190m',
grass: '\x1b[38;2;124;252;0m',
sunset: '\x1b[38;2;255;111;89m',
dawn: '\x1b[38;2;255;179;167m',
earth: '\x1b[38;2;150;75;0m',
sand: '\x1b[38;2;194;178;128m',
// Neon colors
neonPink: '\x1b[38;2;255;0;255m',
neonBlue: '\x1b[38;2;0;255;255m',
neonGreen: '\x1b[38;2;57;255;20m',
neonOrange: '\x1b[38;2;255;95;0m',
neonYellow: '\x1b[38;2;255;255;0m',
neonPurple: '\x1b[38;2;159;0;255m',
// Pastel colors
pastelPink: '\x1b[38;2;255;209;220m',
pastelBlue: '\x1b[38;2;174;198;207m',
pastelGreen: '\x1b[38;2;119;221;119m',
pastelYellow: '\x1b[38;2;253;253;150m',
pastelPurple: '\x1b[38;2;179;158;181m',
pastelOrange: '\x1b[38;2;255;179;71m'
};
// Backgrounds
this.bg = {};
Object.entries(this.colors).forEach(([name, value]) => {
const code = value.match(/\d+(?:;\d+)*/)?.[0];
if (code) {
this.bg[name] = code.startsWith('38')
? `\x1b[48${code.slice(2)}m`
: `\x1b[${parseInt(code) + 10}m`;
}
});
// Styles
this.styles = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
italic: '\x1b[3m',
underline: '\x1b[4m',
blink: '\x1b[5m',
reverse: '\x1b[7m',
hidden: '\x1b[8m',
strikethrough: '\x1b[9m',
doubleUnderline: '\x1b[21m',
overline: '\x1b[53m'
};
// Create color methods
Object.keys(this.colors).forEach(color => {
this[color] = (text: string): string => this.colors[color] + text + this.styles.reset;
});
// Create background methods
Object.keys(this.bg).forEach(color => {
const methodName = `bg${color.charAt(0).toUpperCase() + color.slice(1)}`;
this[methodName] = (text: string): string =>
this.bg[color] + text + this.styles.reset;
});
// Create style methods
Object.keys(this.styles).forEach(style => {
if (style !== 'reset') {
this[style] = (text: string): string => this.styles[style] + text + this.styles.reset;
}
});
}
rgb(r: number, g: number, b: number): (text: string) => string {
return (text: string): string => `\x1b[38;2;${r};${g};${b}m${text}${this.styles.reset}`;
}
bgRgb(r: number, g: number, b: number): (text: string) => string {
return (text: string): string => `\x1b[48;2;${r};${g};${b}m${text}${this.styles.reset}`;
}
private hslToRgb(h: number, s: number, l: number): [number, number, number] {
h /= 360;
s /= 100;
l /= 100;
let r: number, g: number, b: number;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p: number, q: number, t: number): number => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
hsl(h: number, s: number, l: number): (text: string) => string {
const [r, g, b] = this.hslToRgb(h, s, l);
return this.rgb(r, g, b);
}
bgHsl(h: number, s: number, l: number): (text: string) => string {
const [r, g, b] = this.hslToRgb(h, s, l);
return this.bgRgb(r, g, b);
}
rainbow(text: string): string {
const colors = [
'red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'
];
return text.split('').map((char, i) => {
const color = colors[i % colors.length];
return this.colors[color] + char;
}).join('') + this.styles.reset;
}
gradient(text: string, startColor: RGB, endColor: RGB): string {
const chars = text.split('');
const steps = chars.length;
return chars.map((char, i) => {
const r = Math.floor(startColor.r + (endColor.r - startColor.r) * (i / steps));
const g = Math.floor(startColor.g + (endColor.g - startColor.g) * (i / steps));
const b = Math.floor(startColor.b + (endColor.b - startColor.b) * (i / steps));
return this.rgb(r, g, b)(char);
}).join('');
}
multiGradient(text: string, colors: RGB[]): string {
const chars = text.split('');
const segments = colors.length - 1;
const charsPerSegment = Math.ceil(chars.length / segments);
return chars.map((char, i) => {
const segment = Math.min(Math.floor(i / charsPerSegment), segments - 1);
const startColor = colors[segment];
const endColor = colors[segment + 1];
const segmentPosition = (i % charsPerSegment) / charsPerSegment;
const r = Math.floor(startColor.r + (endColor.r - startColor.r) * segmentPosition);
const g = Math.floor(startColor.g + (endColor.g - startColor.g) * segmentPosition);
const b = Math.floor(startColor.b + (endColor.b - startColor.b) * segmentPosition);
return this.rgb(r, g, b)(char);
}).join('') + this.styles.reset;
}
neon(text: string, color: NeonColor = 'blue'): string {
const neonColors: { [key in NeonColor]: (text: string) => string } = {
blue: this.neonBlue,
pink: this.neonPink,
green: this.neonGreen,
orange: this.neonOrange,
yellow: this.neonYellow,
purple: this.neonPurple
};
const colorFn = neonColors[color] || neonColors.blue;
return this.bold(this.blink(colorFn(text)));
}
box(text: string, style: BoxStyle = 'single'): string {
const styles: { [key in BoxStyle]: [string, string, string, string, string, string] } = {
single: ['┌', '─', '┐', '│', '└', '┘'],
double: ['╔', '═', '╗', '║', '╚', '╝'],
round: ['╭', '─', '╮', '│', '╰', '╯'],
bold: ['┏', '━', '┓', '┃', '┗', '┛']
};
const [tl, t, tr, v, bl, br] = styles[style] || styles.single;
const width = text.length + 2;
const top = `${tl}${t.repeat(width)}${tr}`;
const middle = `${v} ${text} ${v}`;
const bottom = `${bl}${t.repeat(width)}${br}`;
return `${top}\n${middle}\n${bottom}`;
}
chain(...styles: string[]): (text: string) => string {
return (text: string): string => styles.reduce((acc, style) => {
if (typeof this[style] === 'function') {
return this[style](acc);
}
return acc;
}, text);
}
}
// Create and export a singleton instance
const brighten = new Brighten();
export default brighten;