@teammates/consolonia 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/__tests__/ansi.test.d.ts +1 -0
- package/dist/__tests__/ansi.test.js +520 -0
- package/dist/__tests__/chat-view.test.d.ts +4 -0
- package/dist/__tests__/chat-view.test.js +480 -0
- package/dist/__tests__/drawing.test.d.ts +4 -0
- package/dist/__tests__/drawing.test.js +426 -0
- package/dist/__tests__/input.test.d.ts +5 -0
- package/dist/__tests__/input.test.js +911 -0
- package/dist/__tests__/layout.test.d.ts +4 -0
- package/dist/__tests__/layout.test.js +689 -0
- package/dist/__tests__/pixel.test.d.ts +1 -0
- package/dist/__tests__/pixel.test.js +674 -0
- package/dist/__tests__/render.test.d.ts +1 -0
- package/dist/__tests__/render.test.js +400 -0
- package/dist/__tests__/styled.test.d.ts +4 -0
- package/dist/__tests__/styled.test.js +149 -0
- package/dist/__tests__/widgets.test.d.ts +5 -0
- package/dist/__tests__/widgets.test.js +924 -0
- package/dist/ansi/esc.d.ts +61 -0
- package/dist/ansi/esc.js +85 -0
- package/dist/ansi/output.d.ts +66 -0
- package/dist/ansi/output.js +192 -0
- package/dist/ansi/strip.d.ts +16 -0
- package/dist/ansi/strip.js +74 -0
- package/dist/app.d.ts +68 -0
- package/dist/app.js +297 -0
- package/dist/drawing/clip.d.ts +23 -0
- package/dist/drawing/clip.js +67 -0
- package/dist/drawing/context.d.ts +77 -0
- package/dist/drawing/context.js +275 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +63 -0
- package/dist/input/escape-matcher.d.ts +27 -0
- package/dist/input/escape-matcher.js +253 -0
- package/dist/input/events.d.ts +49 -0
- package/dist/input/events.js +17 -0
- package/dist/input/index.d.ts +15 -0
- package/dist/input/index.js +14 -0
- package/dist/input/matcher.d.ts +23 -0
- package/dist/input/matcher.js +14 -0
- package/dist/input/mouse-matcher.d.ts +27 -0
- package/dist/input/mouse-matcher.js +142 -0
- package/dist/input/paste-matcher.d.ts +23 -0
- package/dist/input/paste-matcher.js +104 -0
- package/dist/input/processor.d.ts +51 -0
- package/dist/input/processor.js +145 -0
- package/dist/input/raw-mode.d.ts +13 -0
- package/dist/input/raw-mode.js +24 -0
- package/dist/input/text-matcher.d.ts +14 -0
- package/dist/input/text-matcher.js +32 -0
- package/dist/layout/box.d.ts +33 -0
- package/dist/layout/box.js +92 -0
- package/dist/layout/column.d.ts +21 -0
- package/dist/layout/column.js +90 -0
- package/dist/layout/control.d.ts +73 -0
- package/dist/layout/control.js +215 -0
- package/dist/layout/row.d.ts +21 -0
- package/dist/layout/row.js +95 -0
- package/dist/layout/stack.d.ts +18 -0
- package/dist/layout/stack.js +64 -0
- package/dist/layout/types.d.ts +27 -0
- package/dist/layout/types.js +4 -0
- package/dist/pixel/background.d.ts +16 -0
- package/dist/pixel/background.js +16 -0
- package/dist/pixel/box-pattern.d.ts +38 -0
- package/dist/pixel/box-pattern.js +57 -0
- package/dist/pixel/buffer.d.ts +25 -0
- package/dist/pixel/buffer.js +51 -0
- package/dist/pixel/color.d.ts +48 -0
- package/dist/pixel/color.js +92 -0
- package/dist/pixel/foreground.d.ts +31 -0
- package/dist/pixel/foreground.js +64 -0
- package/dist/pixel/pixel.d.ts +21 -0
- package/dist/pixel/pixel.js +38 -0
- package/dist/pixel/symbol.d.ts +38 -0
- package/dist/pixel/symbol.js +192 -0
- package/dist/render/regions.d.ts +54 -0
- package/dist/render/regions.js +102 -0
- package/dist/render/render-target.d.ts +42 -0
- package/dist/render/render-target.js +118 -0
- package/dist/styled.d.ts +113 -0
- package/dist/styled.js +176 -0
- package/dist/widgets/border.d.ts +34 -0
- package/dist/widgets/border.js +121 -0
- package/dist/widgets/chat-view.d.ts +239 -0
- package/dist/widgets/chat-view.js +993 -0
- package/dist/widgets/interview.d.ts +87 -0
- package/dist/widgets/interview.js +187 -0
- package/dist/widgets/markdown.d.ts +87 -0
- package/dist/widgets/markdown.js +611 -0
- package/dist/widgets/panel.d.ts +19 -0
- package/dist/widgets/panel.js +35 -0
- package/dist/widgets/scroll-view.d.ts +43 -0
- package/dist/widgets/scroll-view.js +182 -0
- package/dist/widgets/styled-text.d.ts +38 -0
- package/dist/widgets/styled-text.js +183 -0
- package/dist/widgets/syntax.d.ts +37 -0
- package/dist/widgets/syntax.js +670 -0
- package/dist/widgets/text-input.d.ts +121 -0
- package/dist/widgets/text-input.js +618 -0
- package/dist/widgets/text.d.ts +34 -0
- package/dist/widgets/text.js +168 -0
- package/package.json +45 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack — z-stacking container.
|
|
3
|
+
*
|
|
4
|
+
* All children overlap the same area. They are rendered back to front
|
|
5
|
+
* (first child = bottom, last child = top).
|
|
6
|
+
*/
|
|
7
|
+
import type { DrawingContext } from "../drawing/context.js";
|
|
8
|
+
import { Control } from "./control.js";
|
|
9
|
+
import type { Constraint, Rect, Size } from "./types.js";
|
|
10
|
+
export interface StackOptions {
|
|
11
|
+
children?: Control[];
|
|
12
|
+
}
|
|
13
|
+
export declare class Stack extends Control {
|
|
14
|
+
constructor(options?: StackOptions);
|
|
15
|
+
measure(constraint: Constraint): Size;
|
|
16
|
+
arrange(rect: Rect): void;
|
|
17
|
+
render(ctx: DrawingContext): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack — z-stacking container.
|
|
3
|
+
*
|
|
4
|
+
* All children overlap the same area. They are rendered back to front
|
|
5
|
+
* (first child = bottom, last child = top).
|
|
6
|
+
*/
|
|
7
|
+
import { Control, clampSize } from "./control.js";
|
|
8
|
+
export class Stack extends Control {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
super();
|
|
11
|
+
if (options.children) {
|
|
12
|
+
for (const child of options.children) {
|
|
13
|
+
this.addChild(child);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// ── Layout ────────────────────────────────────────────────────────
|
|
18
|
+
measure(constraint) {
|
|
19
|
+
const visible = this.children.filter((c) => c.visible);
|
|
20
|
+
let maxWidth = 0;
|
|
21
|
+
let maxHeight = 0;
|
|
22
|
+
for (const child of visible) {
|
|
23
|
+
const childSize = child.measure(constraint);
|
|
24
|
+
maxWidth = Math.max(maxWidth, childSize.width);
|
|
25
|
+
maxHeight = Math.max(maxHeight, childSize.height);
|
|
26
|
+
}
|
|
27
|
+
const size = clampSize({ width: maxWidth, height: maxHeight }, constraint);
|
|
28
|
+
this.desiredSize = size;
|
|
29
|
+
return size;
|
|
30
|
+
}
|
|
31
|
+
arrange(rect) {
|
|
32
|
+
this.bounds = rect;
|
|
33
|
+
for (const child of this.children) {
|
|
34
|
+
if (!child.visible)
|
|
35
|
+
continue;
|
|
36
|
+
// Every child gets the full rect (positioned at origin within this stack)
|
|
37
|
+
child.arrange({
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 0,
|
|
40
|
+
width: rect.width,
|
|
41
|
+
height: rect.height,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// ── Render ────────────────────────────────────────────────────────
|
|
46
|
+
render(ctx) {
|
|
47
|
+
// Render back to front: first child is at the bottom, last is on top
|
|
48
|
+
for (const child of this.children) {
|
|
49
|
+
if (!child.visible)
|
|
50
|
+
continue;
|
|
51
|
+
ctx.pushClip({
|
|
52
|
+
x: child.bounds.x,
|
|
53
|
+
y: child.bounds.y,
|
|
54
|
+
width: child.bounds.width,
|
|
55
|
+
height: child.bounds.height,
|
|
56
|
+
});
|
|
57
|
+
ctx.pushTranslate(child.bounds.x, child.bounds.y);
|
|
58
|
+
child.render(ctx);
|
|
59
|
+
child.dirty = false;
|
|
60
|
+
ctx.popTranslate();
|
|
61
|
+
ctx.popClip();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core layout geometry types.
|
|
3
|
+
*/
|
|
4
|
+
/** A 2D size. */
|
|
5
|
+
export interface Size {
|
|
6
|
+
readonly width: number;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
}
|
|
9
|
+
/** A 2D point. */
|
|
10
|
+
export interface Point {
|
|
11
|
+
readonly x: number;
|
|
12
|
+
readonly y: number;
|
|
13
|
+
}
|
|
14
|
+
/** A rectangle defined by origin and size. */
|
|
15
|
+
export interface Rect {
|
|
16
|
+
readonly x: number;
|
|
17
|
+
readonly y: number;
|
|
18
|
+
readonly width: number;
|
|
19
|
+
readonly height: number;
|
|
20
|
+
}
|
|
21
|
+
/** Layout constraints with minimum and maximum bounds. */
|
|
22
|
+
export interface Constraint {
|
|
23
|
+
readonly minWidth: number;
|
|
24
|
+
readonly minHeight: number;
|
|
25
|
+
readonly maxWidth: number;
|
|
26
|
+
readonly maxHeight: number;
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel background: just a color.
|
|
3
|
+
*/
|
|
4
|
+
import { type Color } from "./color.js";
|
|
5
|
+
/** Background of a single pixel cell. */
|
|
6
|
+
export interface PixelBackground {
|
|
7
|
+
readonly color: Color;
|
|
8
|
+
}
|
|
9
|
+
/** Create a PixelBackground. */
|
|
10
|
+
export declare function background(color?: Color): PixelBackground;
|
|
11
|
+
/**
|
|
12
|
+
* Blend an upper background over a lower background using alpha compositing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function blendBackground(above: PixelBackground, below: PixelBackground): PixelBackground;
|
|
15
|
+
/** An empty (transparent) background. */
|
|
16
|
+
export declare const EMPTY_BACKGROUND: PixelBackground;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel background: just a color.
|
|
3
|
+
*/
|
|
4
|
+
import { colorBlend, TRANSPARENT } from "./color.js";
|
|
5
|
+
/** Create a PixelBackground. */
|
|
6
|
+
export function background(color = TRANSPARENT) {
|
|
7
|
+
return { color };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Blend an upper background over a lower background using alpha compositing.
|
|
11
|
+
*/
|
|
12
|
+
export function blendBackground(above, below) {
|
|
13
|
+
return { color: colorBlend(below.color, above.color) };
|
|
14
|
+
}
|
|
15
|
+
/** An empty (transparent) background. */
|
|
16
|
+
export const EMPTY_BACKGROUND = { color: TRANSPARENT };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Box-drawing pattern system ported from Consolonia's BoxPattern.cs.
|
|
3
|
+
*
|
|
4
|
+
* A box pattern is a 4-bit bitmask encoding which sides of a cell have
|
|
5
|
+
* line segments. The bits correspond to:
|
|
6
|
+
* bit 0 (0x1) = UP
|
|
7
|
+
* bit 1 (0x2) = RIGHT
|
|
8
|
+
* bit 2 (0x4) = DOWN
|
|
9
|
+
* bit 3 (0x8) = LEFT
|
|
10
|
+
*
|
|
11
|
+
* This maps cleanly to Unicode box-drawing characters for single-line style.
|
|
12
|
+
*/
|
|
13
|
+
/** Direction bit flags. */
|
|
14
|
+
export declare const UP = 1;
|
|
15
|
+
export declare const RIGHT = 2;
|
|
16
|
+
export declare const DOWN = 4;
|
|
17
|
+
export declare const LEFT = 8;
|
|
18
|
+
/** A box pattern is a 4-bit bitmask (0-15). */
|
|
19
|
+
export type BoxPattern = number;
|
|
20
|
+
/** No lines in any direction. */
|
|
21
|
+
export declare const BOX_NONE: BoxPattern;
|
|
22
|
+
/**
|
|
23
|
+
* Lookup table from 4-bit bitmask to Unicode box-drawing character (single-line style).
|
|
24
|
+
*
|
|
25
|
+
* Index 0 (no bits) maps to a space since there is no line to draw.
|
|
26
|
+
*/
|
|
27
|
+
export declare const BOX_CHARS: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* Get the box-drawing character for a given pattern.
|
|
30
|
+
*/
|
|
31
|
+
export declare function boxChar(pattern: BoxPattern): string;
|
|
32
|
+
/**
|
|
33
|
+
* Merge two box patterns by ORing their bitmasks together.
|
|
34
|
+
* This produces smart corners where lines meet — e.g., merging
|
|
35
|
+
* a horizontal line (RIGHT|LEFT) with a vertical line (UP|DOWN)
|
|
36
|
+
* yields a cross (┼).
|
|
37
|
+
*/
|
|
38
|
+
export declare function mergeBoxPatterns(a: BoxPattern, b: BoxPattern): BoxPattern;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Box-drawing pattern system ported from Consolonia's BoxPattern.cs.
|
|
3
|
+
*
|
|
4
|
+
* A box pattern is a 4-bit bitmask encoding which sides of a cell have
|
|
5
|
+
* line segments. The bits correspond to:
|
|
6
|
+
* bit 0 (0x1) = UP
|
|
7
|
+
* bit 1 (0x2) = RIGHT
|
|
8
|
+
* bit 2 (0x4) = DOWN
|
|
9
|
+
* bit 3 (0x8) = LEFT
|
|
10
|
+
*
|
|
11
|
+
* This maps cleanly to Unicode box-drawing characters for single-line style.
|
|
12
|
+
*/
|
|
13
|
+
/** Direction bit flags. */
|
|
14
|
+
export const UP = 0x1;
|
|
15
|
+
export const RIGHT = 0x2;
|
|
16
|
+
export const DOWN = 0x4;
|
|
17
|
+
export const LEFT = 0x8;
|
|
18
|
+
/** No lines in any direction. */
|
|
19
|
+
export const BOX_NONE = 0;
|
|
20
|
+
/**
|
|
21
|
+
* Lookup table from 4-bit bitmask to Unicode box-drawing character (single-line style).
|
|
22
|
+
*
|
|
23
|
+
* Index 0 (no bits) maps to a space since there is no line to draw.
|
|
24
|
+
*/
|
|
25
|
+
export const BOX_CHARS = [
|
|
26
|
+
" ", // 0b0000 = none
|
|
27
|
+
"\u2502", // 0b0001 = UP │
|
|
28
|
+
"\u2500", // 0b0010 = RIGHT ─
|
|
29
|
+
"\u2514", // 0b0011 = UP+RIGHT └
|
|
30
|
+
"\u2502", // 0b0100 = DOWN │
|
|
31
|
+
"\u2502", // 0b0101 = UP+DOWN │
|
|
32
|
+
"\u250C", // 0b0110 = DOWN+RIGHT ┌
|
|
33
|
+
"\u251C", // 0b0111 = UP+DOWN+RIGHT ├
|
|
34
|
+
"\u2500", // 0b1000 = LEFT ─
|
|
35
|
+
"\u2518", // 0b1001 = UP+LEFT ┘
|
|
36
|
+
"\u2500", // 0b1010 = RIGHT+LEFT ─
|
|
37
|
+
"\u2534", // 0b1011 = UP+RIGHT+LEFT ┴
|
|
38
|
+
"\u2510", // 0b1100 = DOWN+LEFT ┐
|
|
39
|
+
"\u2524", // 0b1101 = UP+DOWN+LEFT ┤
|
|
40
|
+
"\u252C", // 0b1110 = DOWN+RIGHT+LEFT┬
|
|
41
|
+
"\u253C", // 0b1111 = all ┼
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Get the box-drawing character for a given pattern.
|
|
45
|
+
*/
|
|
46
|
+
export function boxChar(pattern) {
|
|
47
|
+
return BOX_CHARS[pattern & 0xf];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Merge two box patterns by ORing their bitmasks together.
|
|
51
|
+
* This produces smart corners where lines meet — e.g., merging
|
|
52
|
+
* a horizontal line (RIGHT|LEFT) with a vertical line (UP|DOWN)
|
|
53
|
+
* yields a cross (┼).
|
|
54
|
+
*/
|
|
55
|
+
export function mergeBoxPatterns(a, b) {
|
|
56
|
+
return (a | b) & 0xf;
|
|
57
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PixelBuffer: a 2D grid of Pixel cells representing a terminal surface.
|
|
3
|
+
*/
|
|
4
|
+
import type { Rect } from "../layout/types.js";
|
|
5
|
+
import type { Pixel } from "./pixel.js";
|
|
6
|
+
/**
|
|
7
|
+
* A 2D pixel buffer backed by a flat array.
|
|
8
|
+
* Cells are stored row-major: index = y * width + x.
|
|
9
|
+
*/
|
|
10
|
+
export declare class PixelBuffer {
|
|
11
|
+
readonly width: number;
|
|
12
|
+
readonly height: number;
|
|
13
|
+
private readonly cells;
|
|
14
|
+
constructor(width: number, height: number);
|
|
15
|
+
/** Check whether (x, y) is within bounds. */
|
|
16
|
+
private inBounds;
|
|
17
|
+
/** Get the pixel at (x, y). Returns PIXEL_SPACE for out-of-bounds. */
|
|
18
|
+
get(x: number, y: number): Pixel;
|
|
19
|
+
/** Set the pixel at (x, y). Silently ignores out-of-bounds writes. */
|
|
20
|
+
set(x: number, y: number, pixel: Pixel): void;
|
|
21
|
+
/** Fill a rectangular region with a pixel value, clipped to buffer bounds. */
|
|
22
|
+
fill(rect: Rect, pixel: Pixel): void;
|
|
23
|
+
/** Reset all cells to PIXEL_SPACE. */
|
|
24
|
+
clear(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PixelBuffer: a 2D grid of Pixel cells representing a terminal surface.
|
|
3
|
+
*/
|
|
4
|
+
import { PIXEL_SPACE } from "./pixel.js";
|
|
5
|
+
/**
|
|
6
|
+
* A 2D pixel buffer backed by a flat array.
|
|
7
|
+
* Cells are stored row-major: index = y * width + x.
|
|
8
|
+
*/
|
|
9
|
+
export class PixelBuffer {
|
|
10
|
+
width;
|
|
11
|
+
height;
|
|
12
|
+
cells;
|
|
13
|
+
constructor(width, height) {
|
|
14
|
+
this.width = width;
|
|
15
|
+
this.height = height;
|
|
16
|
+
this.cells = new Array(width * height);
|
|
17
|
+
this.cells.fill(PIXEL_SPACE);
|
|
18
|
+
}
|
|
19
|
+
/** Check whether (x, y) is within bounds. */
|
|
20
|
+
inBounds(x, y) {
|
|
21
|
+
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
|
22
|
+
}
|
|
23
|
+
/** Get the pixel at (x, y). Returns PIXEL_SPACE for out-of-bounds. */
|
|
24
|
+
get(x, y) {
|
|
25
|
+
if (!this.inBounds(x, y))
|
|
26
|
+
return PIXEL_SPACE;
|
|
27
|
+
return this.cells[y * this.width + x];
|
|
28
|
+
}
|
|
29
|
+
/** Set the pixel at (x, y). Silently ignores out-of-bounds writes. */
|
|
30
|
+
set(x, y, pixel) {
|
|
31
|
+
if (!this.inBounds(x, y))
|
|
32
|
+
return;
|
|
33
|
+
this.cells[y * this.width + x] = pixel;
|
|
34
|
+
}
|
|
35
|
+
/** Fill a rectangular region with a pixel value, clipped to buffer bounds. */
|
|
36
|
+
fill(rect, pixel) {
|
|
37
|
+
const x0 = Math.max(0, rect.x);
|
|
38
|
+
const y0 = Math.max(0, rect.y);
|
|
39
|
+
const x1 = Math.min(this.width, rect.x + rect.width);
|
|
40
|
+
const y1 = Math.min(this.height, rect.y + rect.height);
|
|
41
|
+
for (let y = y0; y < y1; y++) {
|
|
42
|
+
for (let x = x0; x < x1; x++) {
|
|
43
|
+
this.cells[y * this.width + x] = pixel;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Reset all cells to PIXEL_SPACE. */
|
|
48
|
+
clear() {
|
|
49
|
+
this.cells.fill(PIXEL_SPACE);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RGBA color representation and compositing functions.
|
|
3
|
+
* All channel values are integers in the range 0-255.
|
|
4
|
+
*/
|
|
5
|
+
/** RGBA color with each channel in 0-255. */
|
|
6
|
+
export interface Color {
|
|
7
|
+
readonly r: number;
|
|
8
|
+
readonly g: number;
|
|
9
|
+
readonly b: number;
|
|
10
|
+
readonly a: number;
|
|
11
|
+
}
|
|
12
|
+
/** Create a color from RGBA values (all 0-255). */
|
|
13
|
+
export declare function color(r: number, g: number, b: number, a?: number): Color;
|
|
14
|
+
/**
|
|
15
|
+
* Alpha-composite `source` over `target` using standard "over" operator.
|
|
16
|
+
* Both colors use premultiplied-style blending with 0-255 alpha.
|
|
17
|
+
*/
|
|
18
|
+
export declare function colorBlend(target: Color, source: Color): Color;
|
|
19
|
+
/**
|
|
20
|
+
* Brighten a color by a factor (0-1). 1.0 produces white.
|
|
21
|
+
*/
|
|
22
|
+
export declare function colorBrighten(c: Color, factor: number): Color;
|
|
23
|
+
/**
|
|
24
|
+
* Shade (darken) a color by a factor (0-1). 1.0 produces black.
|
|
25
|
+
*/
|
|
26
|
+
export declare function colorShade(c: Color, factor: number): Color;
|
|
27
|
+
export declare const TRANSPARENT: Color;
|
|
28
|
+
export declare const BLACK: Color;
|
|
29
|
+
export declare const RED: Color;
|
|
30
|
+
export declare const GREEN: Color;
|
|
31
|
+
export declare const YELLOW: Color;
|
|
32
|
+
export declare const BLUE: Color;
|
|
33
|
+
export declare const MAGENTA: Color;
|
|
34
|
+
export declare const CYAN: Color;
|
|
35
|
+
export declare const WHITE: Color;
|
|
36
|
+
export declare const BLACK_BRIGHT: Color;
|
|
37
|
+
export declare const RED_BRIGHT: Color;
|
|
38
|
+
export declare const GREEN_BRIGHT: Color;
|
|
39
|
+
export declare const YELLOW_BRIGHT: Color;
|
|
40
|
+
export declare const BLUE_BRIGHT: Color;
|
|
41
|
+
export declare const MAGENTA_BRIGHT: Color;
|
|
42
|
+
export declare const CYAN_BRIGHT: Color;
|
|
43
|
+
export declare const WHITE_BRIGHT: Color;
|
|
44
|
+
/** chalk.gray / chalk.grey = blackBright (ANSI 90) */
|
|
45
|
+
export declare const GRAY: Color;
|
|
46
|
+
export declare const GREY: Color;
|
|
47
|
+
export declare const DARK_GRAY: Color;
|
|
48
|
+
export declare const LIGHT_GRAY: Color;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RGBA color representation and compositing functions.
|
|
3
|
+
* All channel values are integers in the range 0-255.
|
|
4
|
+
*/
|
|
5
|
+
/** Create a color from RGBA values (all 0-255). */
|
|
6
|
+
export function color(r, g, b, a = 255) {
|
|
7
|
+
return { r, g, b, a };
|
|
8
|
+
}
|
|
9
|
+
/** Clamp a value to 0-255 integer range. */
|
|
10
|
+
function clamp(v) {
|
|
11
|
+
return Math.max(0, Math.min(255, Math.round(v)));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Alpha-composite `source` over `target` using standard "over" operator.
|
|
15
|
+
* Both colors use premultiplied-style blending with 0-255 alpha.
|
|
16
|
+
*/
|
|
17
|
+
export function colorBlend(target, source) {
|
|
18
|
+
if (source.a === 0)
|
|
19
|
+
return target;
|
|
20
|
+
if (source.a === 255)
|
|
21
|
+
return source;
|
|
22
|
+
if (target.a === 0)
|
|
23
|
+
return source;
|
|
24
|
+
const sa = source.a / 255;
|
|
25
|
+
const ta = target.a / 255;
|
|
26
|
+
// Standard Porter-Duff "over" operator
|
|
27
|
+
const outA = sa + ta * (1 - sa);
|
|
28
|
+
if (outA === 0)
|
|
29
|
+
return TRANSPARENT;
|
|
30
|
+
const outR = (source.r * sa + target.r * ta * (1 - sa)) / outA;
|
|
31
|
+
const outG = (source.g * sa + target.g * ta * (1 - sa)) / outA;
|
|
32
|
+
const outB = (source.b * sa + target.b * ta * (1 - sa)) / outA;
|
|
33
|
+
return {
|
|
34
|
+
r: clamp(outR),
|
|
35
|
+
g: clamp(outG),
|
|
36
|
+
b: clamp(outB),
|
|
37
|
+
a: clamp(outA * 255),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Brighten a color by a factor (0-1). 1.0 produces white.
|
|
42
|
+
*/
|
|
43
|
+
export function colorBrighten(c, factor) {
|
|
44
|
+
const f = Math.max(0, Math.min(1, factor));
|
|
45
|
+
return {
|
|
46
|
+
r: clamp(c.r + (255 - c.r) * f),
|
|
47
|
+
g: clamp(c.g + (255 - c.g) * f),
|
|
48
|
+
b: clamp(c.b + (255 - c.b) * f),
|
|
49
|
+
a: c.a,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Shade (darken) a color by a factor (0-1). 1.0 produces black.
|
|
54
|
+
*/
|
|
55
|
+
export function colorShade(c, factor) {
|
|
56
|
+
const f = 1 - Math.max(0, Math.min(1, factor));
|
|
57
|
+
return {
|
|
58
|
+
r: clamp(c.r * f),
|
|
59
|
+
g: clamp(c.g * f),
|
|
60
|
+
b: clamp(c.b * f),
|
|
61
|
+
a: c.a,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ── Common color constants ──────────────────────────────────────────
|
|
65
|
+
//
|
|
66
|
+
// Standard colors match chalk's ANSI-16 palette names.
|
|
67
|
+
// RGB values are chosen for truecolor terminals (sent as 24-bit RGB).
|
|
68
|
+
export const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 };
|
|
69
|
+
// ── Standard ANSI colors (chalk: black, red, green, … white) ────────
|
|
70
|
+
export const BLACK = { r: 0, g: 0, b: 0, a: 255 };
|
|
71
|
+
export const RED = { r: 255, g: 0, b: 0, a: 255 };
|
|
72
|
+
export const GREEN = { r: 0, g: 255, b: 0, a: 255 };
|
|
73
|
+
export const YELLOW = { r: 255, g: 255, b: 0, a: 255 };
|
|
74
|
+
export const BLUE = { r: 0, g: 0, b: 255, a: 255 };
|
|
75
|
+
export const MAGENTA = { r: 255, g: 0, b: 255, a: 255 };
|
|
76
|
+
export const CYAN = { r: 0, g: 255, b: 255, a: 255 };
|
|
77
|
+
export const WHITE = { r: 255, g: 255, b: 255, a: 255 };
|
|
78
|
+
// ── Bright ANSI colors (chalk: blackBright … whiteBright) ───────────
|
|
79
|
+
export const BLACK_BRIGHT = { r: 128, g: 128, b: 128, a: 255 };
|
|
80
|
+
export const RED_BRIGHT = { r: 255, g: 85, b: 85, a: 255 };
|
|
81
|
+
export const GREEN_BRIGHT = { r: 85, g: 255, b: 85, a: 255 };
|
|
82
|
+
export const YELLOW_BRIGHT = { r: 255, g: 255, b: 85, a: 255 };
|
|
83
|
+
export const BLUE_BRIGHT = { r: 85, g: 85, b: 255, a: 255 };
|
|
84
|
+
export const MAGENTA_BRIGHT = { r: 255, g: 85, b: 255, a: 255 };
|
|
85
|
+
export const CYAN_BRIGHT = { r: 85, g: 255, b: 255, a: 255 };
|
|
86
|
+
export const WHITE_BRIGHT = { r: 255, g: 255, b: 255, a: 255 };
|
|
87
|
+
// ── Convenience aliases ─────────────────────────────────────────────
|
|
88
|
+
/** chalk.gray / chalk.grey = blackBright (ANSI 90) */
|
|
89
|
+
export const GRAY = BLACK_BRIGHT;
|
|
90
|
+
export const GREY = BLACK_BRIGHT;
|
|
91
|
+
export const DARK_GRAY = { r: 64, g: 64, b: 64, a: 255 };
|
|
92
|
+
export const LIGHT_GRAY = { r: 192, g: 192, b: 192, a: 255 };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel foreground: the character, its color, and text style attributes.
|
|
3
|
+
*/
|
|
4
|
+
import { type Color } from "./color.js";
|
|
5
|
+
import { type Symbol } from "./symbol.js";
|
|
6
|
+
/** Foreground of a single pixel cell. */
|
|
7
|
+
export interface PixelForeground {
|
|
8
|
+
readonly symbol: Symbol;
|
|
9
|
+
readonly color: Color;
|
|
10
|
+
readonly bold: boolean;
|
|
11
|
+
readonly italic: boolean;
|
|
12
|
+
readonly underline: boolean;
|
|
13
|
+
readonly strikethrough: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Create a PixelForeground with defaults for style flags. */
|
|
16
|
+
export declare function foreground(symbol?: Symbol, color?: Color, opts?: {
|
|
17
|
+
bold?: boolean;
|
|
18
|
+
italic?: boolean;
|
|
19
|
+
underline?: boolean;
|
|
20
|
+
strikethrough?: boolean;
|
|
21
|
+
}): PixelForeground;
|
|
22
|
+
/**
|
|
23
|
+
* Blend an upper foreground over a lower foreground.
|
|
24
|
+
*
|
|
25
|
+
* If the upper symbol is a space (and not a box pattern), the lower shows through
|
|
26
|
+
* but colors are still blended. If both have box patterns, they merge via OR.
|
|
27
|
+
* Otherwise the upper symbol wins.
|
|
28
|
+
*/
|
|
29
|
+
export declare function blendForeground(above: PixelForeground, below: PixelForeground): PixelForeground;
|
|
30
|
+
/** An empty foreground (space, transparent, no styles). */
|
|
31
|
+
export declare const EMPTY_FOREGROUND: PixelForeground;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel foreground: the character, its color, and text style attributes.
|
|
3
|
+
*/
|
|
4
|
+
import { boxChar, mergeBoxPatterns } from "./box-pattern.js";
|
|
5
|
+
import { colorBlend, TRANSPARENT } from "./color.js";
|
|
6
|
+
import { EMPTY_SYMBOL } from "./symbol.js";
|
|
7
|
+
/** Create a PixelForeground with defaults for style flags. */
|
|
8
|
+
export function foreground(symbol = EMPTY_SYMBOL, color = TRANSPARENT, opts = {}) {
|
|
9
|
+
return {
|
|
10
|
+
symbol,
|
|
11
|
+
color,
|
|
12
|
+
bold: opts.bold ?? false,
|
|
13
|
+
italic: opts.italic ?? false,
|
|
14
|
+
underline: opts.underline ?? false,
|
|
15
|
+
strikethrough: opts.strikethrough ?? false,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Blend an upper foreground over a lower foreground.
|
|
20
|
+
*
|
|
21
|
+
* If the upper symbol is a space (and not a box pattern), the lower shows through
|
|
22
|
+
* but colors are still blended. If both have box patterns, they merge via OR.
|
|
23
|
+
* Otherwise the upper symbol wins.
|
|
24
|
+
*/
|
|
25
|
+
export function blendForeground(above, below) {
|
|
26
|
+
const aboveSym = above.symbol;
|
|
27
|
+
const belowSym = below.symbol;
|
|
28
|
+
// Both have box patterns: merge them
|
|
29
|
+
if (aboveSym.pattern !== 0 && belowSym.pattern !== 0) {
|
|
30
|
+
const merged = mergeBoxPatterns(aboveSym.pattern, belowSym.pattern);
|
|
31
|
+
const mergedChar = boxChar(merged);
|
|
32
|
+
return {
|
|
33
|
+
symbol: { text: mergedChar, width: 1, pattern: merged },
|
|
34
|
+
color: above.color.a > 0 ? colorBlend(below.color, above.color) : below.color,
|
|
35
|
+
bold: above.bold || below.bold,
|
|
36
|
+
italic: above.italic || below.italic,
|
|
37
|
+
underline: above.underline || below.underline,
|
|
38
|
+
strikethrough: above.strikethrough || below.strikethrough,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Above is a transparent space — lower shows through
|
|
42
|
+
if (aboveSym.text === " " && aboveSym.pattern === 0 && above.color.a === 0) {
|
|
43
|
+
return below;
|
|
44
|
+
}
|
|
45
|
+
// Above has content — it wins, colors are blended
|
|
46
|
+
const blendedColor = colorBlend(below.color, above.color);
|
|
47
|
+
return {
|
|
48
|
+
symbol: aboveSym.text === " " && aboveSym.pattern === 0 ? belowSym : aboveSym,
|
|
49
|
+
color: blendedColor,
|
|
50
|
+
bold: aboveSym.text !== " " ? above.bold : below.bold,
|
|
51
|
+
italic: aboveSym.text !== " " ? above.italic : below.italic,
|
|
52
|
+
underline: aboveSym.text !== " " ? above.underline : below.underline,
|
|
53
|
+
strikethrough: aboveSym.text !== " " ? above.strikethrough : below.strikethrough,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** An empty foreground (space, transparent, no styles). */
|
|
57
|
+
export const EMPTY_FOREGROUND = {
|
|
58
|
+
symbol: EMPTY_SYMBOL,
|
|
59
|
+
color: TRANSPARENT,
|
|
60
|
+
bold: false,
|
|
61
|
+
italic: false,
|
|
62
|
+
underline: false,
|
|
63
|
+
strikethrough: false,
|
|
64
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Pixel represents a single terminal cell: foreground (character + style) over background.
|
|
3
|
+
*/
|
|
4
|
+
import { type PixelBackground } from "./background.js";
|
|
5
|
+
import { type PixelForeground } from "./foreground.js";
|
|
6
|
+
/** A single terminal cell combining foreground and background. */
|
|
7
|
+
export interface Pixel {
|
|
8
|
+
readonly foreground: PixelForeground;
|
|
9
|
+
readonly background: PixelBackground;
|
|
10
|
+
}
|
|
11
|
+
/** Create a Pixel. */
|
|
12
|
+
export declare function pixel(fg?: PixelForeground, bg?: PixelBackground): Pixel;
|
|
13
|
+
/**
|
|
14
|
+
* Composite `above` pixel onto `below` pixel.
|
|
15
|
+
* Both foreground and background are blended independently.
|
|
16
|
+
*/
|
|
17
|
+
export declare function blendPixel(above: Pixel, below: Pixel): Pixel;
|
|
18
|
+
/** A fully transparent, empty pixel. */
|
|
19
|
+
export declare const PIXEL_EMPTY: Pixel;
|
|
20
|
+
/** A space character with transparent colors — the default cell state. */
|
|
21
|
+
export declare const PIXEL_SPACE: Pixel;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Pixel represents a single terminal cell: foreground (character + style) over background.
|
|
3
|
+
*/
|
|
4
|
+
import { blendBackground, EMPTY_BACKGROUND, } from "./background.js";
|
|
5
|
+
import { TRANSPARENT } from "./color.js";
|
|
6
|
+
import { blendForeground, EMPTY_FOREGROUND, } from "./foreground.js";
|
|
7
|
+
import { EMPTY_SYMBOL } from "./symbol.js";
|
|
8
|
+
/** Create a Pixel. */
|
|
9
|
+
export function pixel(fg = EMPTY_FOREGROUND, bg = EMPTY_BACKGROUND) {
|
|
10
|
+
return { foreground: fg, background: bg };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Composite `above` pixel onto `below` pixel.
|
|
14
|
+
* Both foreground and background are blended independently.
|
|
15
|
+
*/
|
|
16
|
+
export function blendPixel(above, below) {
|
|
17
|
+
return {
|
|
18
|
+
foreground: blendForeground(above.foreground, below.foreground),
|
|
19
|
+
background: blendBackground(above.background, below.background),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** A fully transparent, empty pixel. */
|
|
23
|
+
export const PIXEL_EMPTY = {
|
|
24
|
+
foreground: EMPTY_FOREGROUND,
|
|
25
|
+
background: EMPTY_BACKGROUND,
|
|
26
|
+
};
|
|
27
|
+
/** A space character with transparent colors — the default cell state. */
|
|
28
|
+
export const PIXEL_SPACE = {
|
|
29
|
+
foreground: {
|
|
30
|
+
symbol: EMPTY_SYMBOL,
|
|
31
|
+
color: TRANSPARENT,
|
|
32
|
+
bold: false,
|
|
33
|
+
italic: false,
|
|
34
|
+
underline: false,
|
|
35
|
+
strikethrough: false,
|
|
36
|
+
},
|
|
37
|
+
background: EMPTY_BACKGROUND,
|
|
38
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a single cell's character content.
|
|
3
|
+
*/
|
|
4
|
+
import type { BoxPattern } from "./box-pattern.js";
|
|
5
|
+
/** A single terminal cell's character data. */
|
|
6
|
+
export interface Symbol {
|
|
7
|
+
/** The character or grapheme cluster to display. */
|
|
8
|
+
readonly text: string;
|
|
9
|
+
/** Display width: 1 for normal chars, 2 for wide (CJK) chars. */
|
|
10
|
+
readonly width: 1 | 2;
|
|
11
|
+
/** Box-drawing pattern bitmask. 0 for normal characters. */
|
|
12
|
+
readonly pattern: BoxPattern;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if a code point is zero-width (invisible) and should be skipped
|
|
16
|
+
* during rendering. These characters have no visual representation and
|
|
17
|
+
* occupy zero terminal columns. If drawn as individual cells, terminals
|
|
18
|
+
* typically show them as "missing glyph" boxes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isZeroWidth(codePoint: number): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Determine the display width of a single character.
|
|
23
|
+
* Returns 2 for wide characters (CJK, fullwidth forms, emoji), 1 otherwise.
|
|
24
|
+
*/
|
|
25
|
+
export declare function charWidth(codePoint: number): 1 | 2;
|
|
26
|
+
/**
|
|
27
|
+
* Calculate the display width of a string (sum of charWidth per code point).
|
|
28
|
+
* Zero-width characters (variation selectors, ZWJ, etc.) are excluded.
|
|
29
|
+
* Useful for layout and wrapping where terminal column count matters.
|
|
30
|
+
*/
|
|
31
|
+
export declare function stringDisplayWidth(text: string): number;
|
|
32
|
+
/**
|
|
33
|
+
* Create a Symbol from a text string.
|
|
34
|
+
* Width is auto-detected from the first code point.
|
|
35
|
+
*/
|
|
36
|
+
export declare function sym(text: string, pattern?: BoxPattern): Symbol;
|
|
37
|
+
/** An empty symbol (space character). */
|
|
38
|
+
export declare const EMPTY_SYMBOL: Symbol;
|