textui/ansi.ts

92 lines
2.2 KiB
TypeScript
Raw Normal View History

2021-05-13 22:04:47 +00:00
import { BufferLocation, BufferSize, Char, Color } from "./base.ts";
import { Display } from "./display.ts";
2021-05-11 21:20:33 +00:00
/**
* ANSI terminal display
*/
export class AnsiTerminalDisplay implements Display {
2021-06-24 22:41:34 +00:00
private palette_bg: readonly Uint8Array[] = [];
private palette_fg: readonly Uint8Array[] = [];
2021-06-24 23:09:05 +00:00
private width = 1;
private state = { x: -1, y: -1, f: -1, b: -1 }; // current location and color
2021-06-24 22:41:34 +00:00
2021-05-13 22:04:47 +00:00
constructor(
private writer: Deno.Writer = Deno.stdout,
private reader: Deno.Reader = Deno.stdin,
) {
}
async getSize(): Promise<BufferSize> {
const size = Deno.consoleSize(Deno.stdout.rid);
2021-06-24 23:09:05 +00:00
this.width = size.columns;
2021-05-13 22:04:47 +00:00
return {
w: size.columns,
h: size.rows,
};
2021-05-11 21:20:33 +00:00
}
async setupPalette(colors: readonly Color[]): Promise<readonly Color[]> {
2021-06-24 22:41:34 +00:00
// TODO handle not fully rgb compatible terminals
const cr = (x: number) => Math.round(x * 255);
this.palette_bg = colors.map((col) =>
escape(`[48;2;${cr(col.r)};${cr(col.g)};${cr(col.b)}m`)
);
this.palette_fg = colors.map((col) =>
escape(`[38;2;${cr(col.r)};${cr(col.g)};${cr(col.b)}m`)
);
2021-05-11 21:20:33 +00:00
return colors;
}
async clear(): Promise<void> {
await this.writer.write(CLEAR);
}
2021-05-13 22:04:47 +00:00
async setChar(at: BufferLocation, char: Char): Promise<void> {
2021-06-24 23:09:05 +00:00
let { x, y, f, b } = this.state;
if (f != char.fg) {
f = char.fg;
2021-06-27 17:46:27 +00:00
const col = this.palette_fg[f];
if (col) {
await this.writer.write(col);
}
2021-06-24 23:09:05 +00:00
}
if (b != char.bg) {
b = char.bg;
2021-06-27 17:46:27 +00:00
const col = this.palette_bg[b];
if (col) {
await this.writer.write(col);
}
2021-06-24 23:09:05 +00:00
}
if (x != at.x || y != at.y) {
x = at.x;
y = at.y;
2021-06-27 17:46:27 +00:00
await this.writer.write(escape(`[${y + 1};${x + 1}H`));
2021-06-24 23:09:05 +00:00
}
await this.writer.write(new TextEncoder().encode(char.ch));
x += 1;
2021-06-27 17:46:27 +00:00
if (x >= this.width) {
2021-06-24 23:09:05 +00:00
x = 0;
y += 1;
}
this.state = { x, y, f, b };
2021-05-13 22:04:47 +00:00
}
2021-06-27 17:46:27 +00:00
/**
* Force the display size for subsequent prints
*/
forceSize(size: BufferSize) {
this.width = size.w;
}
2021-05-11 21:20:33 +00:00
}
function escape(sequence: string): Uint8Array {
return new Uint8Array([0x1B, ...new TextEncoder().encode(sequence)]);
}
const CLEAR = escape("[2J");