@tuidom/core 0.3.0 → 0.4.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/dist/common/colorUtils.d.ts +49 -0
- package/dist/common/colorUtils.js +101 -0
- package/dist/dom/styles/tuiStyle.d.ts +6 -0
- package/dist/dom/styles/tuiStyle.js +11 -3
- package/dist/dom/tuiElement.d.ts +9 -2
- package/dist/dom/tuiElement.js +23 -10
- package/dist/rendering/grid.d.ts +6 -0
- package/dist/rendering/grid.js +20 -10
- package/package.json +1 -1
|
@@ -3,17 +3,66 @@
|
|
|
3
3
|
* Must be negative so it's never confused with a packed RGB value (0x000000–0xFFFFFF).
|
|
4
4
|
*/
|
|
5
5
|
export declare const DEFAULT_COLOR = -1;
|
|
6
|
+
/**
|
|
7
|
+
* Полностью прозрачный цвет (`#RRGGBB00`, CSS `transparent`): при наложении
|
|
8
|
+
* ничего не меняет, а как собственный bg элемента — не красит. Сентинел, а не
|
|
9
|
+
* упаковка с альфой 0: старший байт 0 занят непрозрачными 24-битными числами.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TRANSPARENT_COLOR = -2;
|
|
6
12
|
/** Pack three 8-bit channels into a single 24-bit integer. */
|
|
7
13
|
export declare function packRgb(r: number, g: number, b: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Упаковывает цвет с альфой (0..255). Непрозрачный (`a === 255`) даёт обычное
|
|
16
|
+
* 24-битное число — то же, что {@link packRgb}; `a === 0` — {@link TRANSPARENT_COLOR};
|
|
17
|
+
* промежуточная альфа кладётся в старший байт (`0xAARRGGBB`, беззнаковое
|
|
18
|
+
* значение > 0xFFFFFF). Так весь существующий код с 24-битными литералами
|
|
19
|
+
* остаётся валидным без миграции, а полупрозрачность — отличима одним сравнением.
|
|
20
|
+
*/
|
|
21
|
+
export declare function packRgba(r: number, g: number, b: number, a: number): number;
|
|
8
22
|
/** Extract the red channel (bits 16–23). */
|
|
9
23
|
export declare function unpackR(color: number): number;
|
|
10
24
|
/** Extract the green channel (bits 8–15). */
|
|
11
25
|
export declare function unpackG(color: number): number;
|
|
12
26
|
/** Extract the blue channel (bits 0–7). */
|
|
13
27
|
export declare function unpackB(color: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Альфа цвета: 255 у 24-битных чисел и сентинелов (`DEFAULT_COLOR` — цвет
|
|
30
|
+
* терминала, он непрозрачен), 0 у {@link TRANSPARENT_COLOR}, иначе старший байт.
|
|
31
|
+
*/
|
|
32
|
+
export declare function unpackA(color: number): number;
|
|
33
|
+
/** true для полупрозрачного цвета (альфа 1..254) — того, что требует композитинга с подложкой. */
|
|
34
|
+
export declare function isTranslucent(color: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* true, если число — легальное значение цвета: 24-битный RGB, упаковка с
|
|
37
|
+
* альфой, {@link DEFAULT_COLOR} или {@link TRANSPARENT_COLOR}. Сентинелы
|
|
38
|
+
* каскада (`INHERITED_*`) и мусор — false.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isColorValue(color: number): boolean;
|
|
14
41
|
/**
|
|
15
42
|
* Накладывает полупрозрачный `fg` на непрозрачную подложку `bg` и возвращает
|
|
16
43
|
* непрозрачный результат: терминал альфы не умеет, поэтому композитинг
|
|
17
44
|
* выполняется заранее (`alpha` — доля 0..1).
|
|
18
45
|
*/
|
|
19
46
|
export declare function blendRgb(fg: number, bg: number, alpha: number): number;
|
|
47
|
+
/**
|
|
48
|
+
* Композитинг «`color` поверх `under`» (source-over): результат всегда
|
|
49
|
+
* непрозрачен или `DEFAULT_COLOR`. Непрозрачный `color` (и `DEFAULT_COLOR`)
|
|
50
|
+
* возвращается как есть — путь нулевой стоимости, одно сравнение;
|
|
51
|
+
* {@link TRANSPARENT_COLOR} оставляет подложку. Полупрозрачный цвет смешивается
|
|
52
|
+
* с `under` по альфе; если подложка — `DEFAULT_COLOR` (цвет терминала, нам
|
|
53
|
+
* неизвестен), смешивать не с чем — альфа отбрасывается, цвет ложится непрозрачным.
|
|
54
|
+
*/
|
|
55
|
+
export declare function compositeOver(color: number, under: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Разбирает CSS-подобный hex: `#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA`
|
|
58
|
+
* (решётка необязательна, регистр любой) в упакованный цвет (см.
|
|
59
|
+
* {@link packRgba}). Некорректная строка — throw: цвет темы с опечаткой
|
|
60
|
+
* должен падать на разборе, а не красить в чёрный.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseHexColor(hex: string): number;
|
|
63
|
+
/**
|
|
64
|
+
* Обратное к {@link parseHexColor}: `#rrggbb` для непрозрачного, `#rrggbbaa`
|
|
65
|
+
* с альфой, `#00000000` для {@link TRANSPARENT_COLOR}. `DEFAULT_COLOR` hex не
|
|
66
|
+
* имеет — throw.
|
|
67
|
+
*/
|
|
68
|
+
export declare function formatHexColor(color: number): string;
|
|
@@ -3,10 +3,34 @@
|
|
|
3
3
|
* Must be negative so it's never confused with a packed RGB value (0x000000–0xFFFFFF).
|
|
4
4
|
*/
|
|
5
5
|
export const DEFAULT_COLOR = -1;
|
|
6
|
+
/**
|
|
7
|
+
* Полностью прозрачный цвет (`#RRGGBB00`, CSS `transparent`): при наложении
|
|
8
|
+
* ничего не меняет, а как собственный bg элемента — не красит. Сентинел, а не
|
|
9
|
+
* упаковка с альфой 0: старший байт 0 занят непрозрачными 24-битными числами.
|
|
10
|
+
*/
|
|
11
|
+
export const TRANSPARENT_COLOR = -2;
|
|
12
|
+
/** Максимальное упакованное значение с альфой (`0xFE_FFFFFF`); всё выше — не цвет. */
|
|
13
|
+
const MAX_PACKED_COLOR = 0xfeffffff;
|
|
14
|
+
/** Множитель старшего байта — альфа хранится через умножение, а не `<<`, чтобы не уйти в отрицательные. */
|
|
15
|
+
const ALPHA_UNIT = 0x1000000;
|
|
6
16
|
/** Pack three 8-bit channels into a single 24-bit integer. */
|
|
7
17
|
export function packRgb(r, g, b) {
|
|
8
18
|
return (r << 16) | (g << 8) | b;
|
|
9
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Упаковывает цвет с альфой (0..255). Непрозрачный (`a === 255`) даёт обычное
|
|
22
|
+
* 24-битное число — то же, что {@link packRgb}; `a === 0` — {@link TRANSPARENT_COLOR};
|
|
23
|
+
* промежуточная альфа кладётся в старший байт (`0xAARRGGBB`, беззнаковое
|
|
24
|
+
* значение > 0xFFFFFF). Так весь существующий код с 24-битными литералами
|
|
25
|
+
* остаётся валидным без миграции, а полупрозрачность — отличима одним сравнением.
|
|
26
|
+
*/
|
|
27
|
+
export function packRgba(r, g, b, a) {
|
|
28
|
+
if (a >= 0xff)
|
|
29
|
+
return packRgb(r, g, b);
|
|
30
|
+
if (a <= 0)
|
|
31
|
+
return TRANSPARENT_COLOR;
|
|
32
|
+
return a * ALPHA_UNIT + packRgb(r, g, b);
|
|
33
|
+
}
|
|
10
34
|
/** Extract the red channel (bits 16–23). */
|
|
11
35
|
export function unpackR(color) {
|
|
12
36
|
return (color >> 16) & 0xff;
|
|
@@ -19,6 +43,31 @@ export function unpackG(color) {
|
|
|
19
43
|
export function unpackB(color) {
|
|
20
44
|
return color & 0xff;
|
|
21
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Альфа цвета: 255 у 24-битных чисел и сентинелов (`DEFAULT_COLOR` — цвет
|
|
48
|
+
* терминала, он непрозрачен), 0 у {@link TRANSPARENT_COLOR}, иначе старший байт.
|
|
49
|
+
*/
|
|
50
|
+
export function unpackA(color) {
|
|
51
|
+
if (color === TRANSPARENT_COLOR)
|
|
52
|
+
return 0;
|
|
53
|
+
if (color <= 0xffffff)
|
|
54
|
+
return 0xff;
|
|
55
|
+
return color >>> 24;
|
|
56
|
+
}
|
|
57
|
+
/** true для полупрозрачного цвета (альфа 1..254) — того, что требует композитинга с подложкой. */
|
|
58
|
+
export function isTranslucent(color) {
|
|
59
|
+
return color > 0xffffff;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* true, если число — легальное значение цвета: 24-битный RGB, упаковка с
|
|
63
|
+
* альфой, {@link DEFAULT_COLOR} или {@link TRANSPARENT_COLOR}. Сентинелы
|
|
64
|
+
* каскада (`INHERITED_*`) и мусор — false.
|
|
65
|
+
*/
|
|
66
|
+
export function isColorValue(color) {
|
|
67
|
+
return (color === DEFAULT_COLOR ||
|
|
68
|
+
color === TRANSPARENT_COLOR ||
|
|
69
|
+
(Number.isInteger(color) && color >= 0 && color <= MAX_PACKED_COLOR));
|
|
70
|
+
}
|
|
22
71
|
/**
|
|
23
72
|
* Накладывает полупрозрачный `fg` на непрозрачную подложку `bg` и возвращает
|
|
24
73
|
* непрозрачный результат: терминал альфы не умеет, поэтому композитинг
|
|
@@ -28,3 +77,55 @@ export function blendRgb(fg, bg, alpha) {
|
|
|
28
77
|
const mix = (f, b) => Math.round(f * alpha + b * (1 - alpha));
|
|
29
78
|
return packRgb(mix(unpackR(fg), unpackR(bg)), mix(unpackG(fg), unpackG(bg)), mix(unpackB(fg), unpackB(bg)));
|
|
30
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Композитинг «`color` поверх `under`» (source-over): результат всегда
|
|
82
|
+
* непрозрачен или `DEFAULT_COLOR`. Непрозрачный `color` (и `DEFAULT_COLOR`)
|
|
83
|
+
* возвращается как есть — путь нулевой стоимости, одно сравнение;
|
|
84
|
+
* {@link TRANSPARENT_COLOR} оставляет подложку. Полупрозрачный цвет смешивается
|
|
85
|
+
* с `under` по альфе; если подложка — `DEFAULT_COLOR` (цвет терминала, нам
|
|
86
|
+
* неизвестен), смешивать не с чем — альфа отбрасывается, цвет ложится непрозрачным.
|
|
87
|
+
*/
|
|
88
|
+
export function compositeOver(color, under) {
|
|
89
|
+
if (color <= 0xffffff)
|
|
90
|
+
return color === TRANSPARENT_COLOR ? under : color;
|
|
91
|
+
const rgb = color & 0xffffff;
|
|
92
|
+
if (under < 0)
|
|
93
|
+
return rgb;
|
|
94
|
+
return blendRgb(rgb, under, (color >>> 24) / 0xff);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Разбирает CSS-подобный hex: `#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA`
|
|
98
|
+
* (решётка необязательна, регистр любой) в упакованный цвет (см.
|
|
99
|
+
* {@link packRgba}). Некорректная строка — throw: цвет темы с опечаткой
|
|
100
|
+
* должен падать на разборе, а не красить в чёрный.
|
|
101
|
+
*/
|
|
102
|
+
export function parseHexColor(hex) {
|
|
103
|
+
const digits = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
104
|
+
const short = digits.length === 3 || digits.length === 4;
|
|
105
|
+
if (!(short || digits.length === 6 || digits.length === 8) || !/^[0-9a-fA-F]+$/.test(digits)) {
|
|
106
|
+
throw new Error(`parseHexColor: некорректный hex-цвет "${hex}"`);
|
|
107
|
+
}
|
|
108
|
+
const channel = (i) => {
|
|
109
|
+
if (short) {
|
|
110
|
+
const d = parseInt(digits[i], 16);
|
|
111
|
+
return d * 16 + d;
|
|
112
|
+
}
|
|
113
|
+
return parseInt(digits.slice(i * 2, i * 2 + 2), 16);
|
|
114
|
+
};
|
|
115
|
+
const hasAlpha = digits.length === 4 || digits.length === 8;
|
|
116
|
+
return packRgba(channel(0), channel(1), channel(2), hasAlpha ? channel(3) : 0xff);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Обратное к {@link parseHexColor}: `#rrggbb` для непрозрачного, `#rrggbbaa`
|
|
120
|
+
* с альфой, `#00000000` для {@link TRANSPARENT_COLOR}. `DEFAULT_COLOR` hex не
|
|
121
|
+
* имеет — throw.
|
|
122
|
+
*/
|
|
123
|
+
export function formatHexColor(color) {
|
|
124
|
+
if (color === DEFAULT_COLOR)
|
|
125
|
+
throw new Error("formatHexColor: DEFAULT_COLOR не имеет hex-представления");
|
|
126
|
+
if (color === TRANSPARENT_COLOR)
|
|
127
|
+
return "#00000000";
|
|
128
|
+
const hex2 = (n) => n.toString(16).padStart(2, "0");
|
|
129
|
+
const rgb = `#${hex2(unpackR(color))}${hex2(unpackG(color))}${hex2(unpackB(color))}`;
|
|
130
|
+
return isTranslucent(color) ? `${rgb}${hex2(unpackA(color))}` : rgb;
|
|
131
|
+
}
|
|
@@ -72,4 +72,10 @@ export declare function mergeStyleVariants(style: TUIStyle, isActive: (selector:
|
|
|
72
72
|
fg: StyleColor | undefined;
|
|
73
73
|
bg: StyleColor | undefined;
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Резолв базовых fg/bg без when-вариантов и var-scope (утилита для тестов и
|
|
77
|
+
* простых потребителей; ядро идёт через `TUIElement.performStyleResolution`,
|
|
78
|
+
* та же модель): цвет с альфой композитится с унаследованным bg, fg — с
|
|
79
|
+
* итоговым bg, наружу уходят непрозрачные значения.
|
|
80
|
+
*/
|
|
75
81
|
export declare function resolveStyle(style: TUIStyle, inherited: ResolvedTUIStyle): ResolvedTUIStyle;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_COLOR } from "../../common/colorUtils.js";
|
|
1
|
+
import { compositeOver, DEFAULT_COLOR } from "../../common/colorUtils.js";
|
|
2
2
|
import { ROOT_VAR_SCOPE } from "./styleTokens.js";
|
|
3
3
|
// ─── Inherited Color Sentinels ───
|
|
4
4
|
// Sentinel values that resolve to the inherited fg/bg from the parent.
|
|
@@ -114,8 +114,16 @@ export function mergeStyleVariants(style, isActive) {
|
|
|
114
114
|
}
|
|
115
115
|
return { fg, bg };
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Резолв базовых fg/bg без when-вариантов и var-scope (утилита для тестов и
|
|
119
|
+
* простых потребителей; ядро идёт через `TUIElement.performStyleResolution`,
|
|
120
|
+
* та же модель): цвет с альфой композитится с унаследованным bg, fg — с
|
|
121
|
+
* итоговым bg, наружу уходят непрозрачные значения.
|
|
122
|
+
*/
|
|
117
123
|
export function resolveStyle(style, inherited) {
|
|
118
|
-
const
|
|
119
|
-
const bg =
|
|
124
|
+
const ownBg = style.bg !== undefined ? resolveStyleColor(style.bg, inherited.fg, inherited.bg) : inherited.bg;
|
|
125
|
+
const bg = compositeOver(ownBg, inherited.bg);
|
|
126
|
+
const ownFg = style.fg !== undefined ? resolveStyleColor(style.fg, inherited.fg, inherited.bg) : inherited.fg;
|
|
127
|
+
const fg = compositeOver(ownFg, bg);
|
|
120
128
|
return { fg, bg };
|
|
121
129
|
}
|
package/dist/dom/tuiElement.d.ts
CHANGED
|
@@ -143,6 +143,8 @@ export declare class TUIElement {
|
|
|
143
143
|
private subtreeStyleDirty;
|
|
144
144
|
private appliedFgValue;
|
|
145
145
|
private appliedBgValue;
|
|
146
|
+
/** Красит ли элемент собственный фон: bg задан своим стилем и не TRANSPARENT_COLOR. */
|
|
147
|
+
private paintsOwnBackgroundValue;
|
|
146
148
|
private styleStatesSet;
|
|
147
149
|
private styleVarsValue;
|
|
148
150
|
private varScopeRef;
|
|
@@ -283,8 +285,9 @@ export declare class TUIElement {
|
|
|
283
285
|
* предков и дефолтов tuidom (STYLE_TOKEN_DEFAULTS). Обычное место — корень:
|
|
284
286
|
* хост транслирует сюда палитру темы одним вызовом (hot-swap = повторный
|
|
285
287
|
* вызов). Таблица заменяется целиком, null — снимает. Значения — только
|
|
286
|
-
* конкретные числа (packed RGB
|
|
287
|
-
*
|
|
288
|
+
* конкретные числа (packed RGB, в том числе с альфой — `packRgba`/
|
|
289
|
+
* `parseHexColor`, | DEFAULT_COLOR | TRANSPARENT_COLOR); сентинелы
|
|
290
|
+
* INHERITED_* нелегальны.
|
|
288
291
|
*/
|
|
289
292
|
setStyleVars(vars: Readonly<Record<string, number>> | null): void;
|
|
290
293
|
/**
|
|
@@ -503,6 +506,10 @@ export declare class TUIElement {
|
|
|
503
506
|
* флагов стиля: собственный фон — это новая поверхность, и атрибуты того,
|
|
504
507
|
* что нарисовано под ней раньше в этом кадре (подчёркивание диагностики под
|
|
505
508
|
* попапом, жирный токен под диалогом), на ней проступать не должны.
|
|
509
|
+
*
|
|
510
|
+
* Полупрозрачный собственный bg уже скомпозичен каскадом с унаследованным
|
|
511
|
+
* (см. {@link performStyleResolution}); `TRANSPARENT_COLOR` (`#RRGGBB00`)
|
|
512
|
+
* считается «фона нет» — как CSS `transparent`, элемент ничего не красит.
|
|
506
513
|
*/
|
|
507
514
|
protected paintOwnBackground(context: RenderContext): void;
|
|
508
515
|
/** true, если фон задан собственным стилем (см. {@link paintOwnBackground}). */
|
package/dist/dom/tuiElement.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { compositeOver, isColorValue, TRANSPARENT_COLOR } from "../common/colorUtils.js";
|
|
2
2
|
import { DisplayLine } from "../common/displayLine.js";
|
|
3
3
|
import { BoxConstraints, Offset, Point, Rect, Size } from "../common/geometryPromitives.js";
|
|
4
4
|
import { StyleFlags } from "../common/styleFlags.js";
|
|
@@ -217,6 +217,8 @@ export class TUIElement {
|
|
|
217
217
|
// вопрос «задан ли цвет собственным стилем» (заливка фона, инспектор).
|
|
218
218
|
appliedFgValue;
|
|
219
219
|
appliedBgValue;
|
|
220
|
+
/** Красит ли элемент собственный фон: bg задан своим стилем и не TRANSPARENT_COLOR. */
|
|
221
|
+
paintsOwnBackgroundValue = false;
|
|
220
222
|
// Активные состояния (hover/focus ведёт ядро, прочие — виджеты). Lazy:
|
|
221
223
|
// у подавляющего большинства элементов состояний нет.
|
|
222
224
|
styleStatesSet = null;
|
|
@@ -571,13 +573,19 @@ export class TUIElement {
|
|
|
571
573
|
const vars = this.styleVarsValue !== null ? extendVarScope(context.vars, this.styleVarsValue) : context.vars;
|
|
572
574
|
this.varScopeRef = vars;
|
|
573
575
|
const describe = () => this.describeForStyleError();
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const
|
|
576
|
+
// Цвет с альфой композитится уже здесь, с унаследованным bg: в
|
|
577
|
+
// resolvedStyle и детям уходят непрозрачные значения, иначе заливка
|
|
578
|
+
// фона и текст с тем же bg наложились бы на ячейку дважды.
|
|
579
|
+
const ownBg = applied.bg !== undefined
|
|
578
580
|
? resolveStyleColor(applied.bg, context.fg, context.bg, vars, describe)
|
|
579
581
|
: context.bg;
|
|
582
|
+
const bg = compositeOver(ownBg, context.bg);
|
|
583
|
+
const ownFg = applied.fg !== undefined
|
|
584
|
+
? resolveStyleColor(applied.fg, context.fg, context.bg, vars, describe)
|
|
585
|
+
: context.fg;
|
|
586
|
+
const fg = compositeOver(ownFg, bg);
|
|
580
587
|
this.resolvedStyleValue = { fg, bg };
|
|
588
|
+
this.paintsOwnBackgroundValue = applied.bg !== undefined && ownBg !== TRANSPARENT_COLOR;
|
|
581
589
|
this.childStyleContext = this.buildChildStyleContext(context);
|
|
582
590
|
}
|
|
583
591
|
this.isStyleDirty = false;
|
|
@@ -636,15 +644,16 @@ export class TUIElement {
|
|
|
636
644
|
* предков и дефолтов tuidom (STYLE_TOKEN_DEFAULTS). Обычное место — корень:
|
|
637
645
|
* хост транслирует сюда палитру темы одним вызовом (hot-swap = повторный
|
|
638
646
|
* вызов). Таблица заменяется целиком, null — снимает. Значения — только
|
|
639
|
-
* конкретные числа (packed RGB
|
|
640
|
-
*
|
|
647
|
+
* конкретные числа (packed RGB, в том числе с альфой — `packRgba`/
|
|
648
|
+
* `parseHexColor`, | DEFAULT_COLOR | TRANSPARENT_COLOR); сентинелы
|
|
649
|
+
* INHERITED_* нелегальны.
|
|
641
650
|
*/
|
|
642
651
|
setStyleVars(vars) {
|
|
643
652
|
if (vars === this.styleVarsValue)
|
|
644
653
|
return;
|
|
645
654
|
if (vars !== null) {
|
|
646
655
|
for (const key of Object.keys(vars)) {
|
|
647
|
-
if (vars[key]
|
|
656
|
+
if (!isColorValue(vars[key])) {
|
|
648
657
|
throw new Error(`${this.describeForStyleError()}.setStyleVars: токен "${key}" содержит сентинел/некорректное значение ${vars[key]} — таблицы принимают только конкретные цвета`);
|
|
649
658
|
}
|
|
650
659
|
}
|
|
@@ -1101,9 +1110,13 @@ export class TUIElement {
|
|
|
1101
1110
|
* флагов стиля: собственный фон — это новая поверхность, и атрибуты того,
|
|
1102
1111
|
* что нарисовано под ней раньше в этом кадре (подчёркивание диагностики под
|
|
1103
1112
|
* попапом, жирный токен под диалогом), на ней проступать не должны.
|
|
1113
|
+
*
|
|
1114
|
+
* Полупрозрачный собственный bg уже скомпозичен каскадом с унаследованным
|
|
1115
|
+
* (см. {@link performStyleResolution}); `TRANSPARENT_COLOR` (`#RRGGBB00`)
|
|
1116
|
+
* считается «фона нет» — как CSS `transparent`, элемент ничего не красит.
|
|
1104
1117
|
*/
|
|
1105
1118
|
paintOwnBackground(context) {
|
|
1106
|
-
if (this.
|
|
1119
|
+
if (!this.paintsOwnBackgroundValue)
|
|
1107
1120
|
return;
|
|
1108
1121
|
const { fg, bg } = this.resolvedStyleValue;
|
|
1109
1122
|
const { width, height } = this.layoutSize;
|
|
@@ -1115,7 +1128,7 @@ export class TUIElement {
|
|
|
1115
1128
|
}
|
|
1116
1129
|
/** true, если фон задан собственным стилем (см. {@link paintOwnBackground}). */
|
|
1117
1130
|
get hasOwnBackground() {
|
|
1118
|
-
return this.
|
|
1131
|
+
return this.paintsOwnBackgroundValue;
|
|
1119
1132
|
}
|
|
1120
1133
|
/**
|
|
1121
1134
|
* Сырые цвета после when-merge, до резолва токенов/сентинелов: что элемент
|
package/dist/rendering/grid.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ export interface CellPatch {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* 2D grid of terminal cells backed by a flat array for cache-friendly access.
|
|
18
|
+
*
|
|
19
|
+
* Ячейки хранят только непрозрачные цвета (или DEFAULT_COLOR): цвет с альфой,
|
|
20
|
+
* пришедший в {@link setCell}/{@link updateCell}, композитится с текущим
|
|
21
|
+
* содержимым ячейки на месте (см. `compositeOver`). Так порядок отрисовки —
|
|
22
|
+
* родитель, дети, оверлеи, патчи подсветок — становится порядком наложения
|
|
23
|
+
* слоёв, как в браузере, а бэкенды/снапшоты/SVG видят уже готовые цвета.
|
|
18
24
|
*/
|
|
19
25
|
export declare class Grid {
|
|
20
26
|
readonly size: Size;
|
package/dist/rendering/grid.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { DEFAULT_COLOR } from "../common/colorUtils.js";
|
|
1
|
+
import { compositeOver, DEFAULT_COLOR } from "../common/colorUtils.js";
|
|
2
2
|
import { Point, Rect, Size } from "../common/geometryPromitives.js";
|
|
3
3
|
import { StyleFlags } from "../common/styleFlags.js";
|
|
4
4
|
import { Cell } from "./cell.js";
|
|
5
5
|
/**
|
|
6
6
|
* 2D grid of terminal cells backed by a flat array for cache-friendly access.
|
|
7
|
+
*
|
|
8
|
+
* Ячейки хранят только непрозрачные цвета (или DEFAULT_COLOR): цвет с альфой,
|
|
9
|
+
* пришедший в {@link setCell}/{@link updateCell}, композитится с текущим
|
|
10
|
+
* содержимым ячейки на месте (см. `compositeOver`). Так порядок отрисовки —
|
|
11
|
+
* родитель, дети, оверлеи, патчи подсветок — становится порядком наложения
|
|
12
|
+
* слоёв, как в браузере, а бэкенды/снапшоты/SVG видят уже готовые цвета.
|
|
7
13
|
*/
|
|
8
14
|
export class Grid {
|
|
9
15
|
size;
|
|
@@ -55,8 +61,8 @@ export class Grid {
|
|
|
55
61
|
/* v8 ignore stop */
|
|
56
62
|
}
|
|
57
63
|
cell.char = char;
|
|
58
|
-
cell.
|
|
59
|
-
cell.
|
|
64
|
+
cell.bg = compositeOver(bg, cell.bg);
|
|
65
|
+
cell.fg = compositeOver(fg, cell.bg);
|
|
60
66
|
cell.style = style;
|
|
61
67
|
cell.width = width;
|
|
62
68
|
// For wide chars, set up the continuation cell
|
|
@@ -73,8 +79,8 @@ export class Grid {
|
|
|
73
79
|
/* v8 ignore stop */
|
|
74
80
|
}
|
|
75
81
|
cont.char = "";
|
|
76
|
-
cont.fg = fg;
|
|
77
|
-
cont.bg = bg;
|
|
82
|
+
cont.fg = cell.fg;
|
|
83
|
+
cont.bg = cell.bg;
|
|
78
84
|
cont.style = style;
|
|
79
85
|
cont.width = 0;
|
|
80
86
|
}
|
|
@@ -108,10 +114,13 @@ export class Grid {
|
|
|
108
114
|
}
|
|
109
115
|
if (patch.char !== undefined)
|
|
110
116
|
cell.char = patch.char;
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
// Композитинг в порядке отрисовки: полупрозрачный bg ложится на то, что
|
|
118
|
+
// уже лежит в ячейке в этом кадре, полупрозрачный fg — на итоговый bg.
|
|
119
|
+
// Непрозрачные цвета проходят как раньше (compositeOver — одно сравнение).
|
|
113
120
|
if (patch.bg !== undefined)
|
|
114
|
-
cell.bg = patch.bg;
|
|
121
|
+
cell.bg = compositeOver(patch.bg, cell.bg);
|
|
122
|
+
if (patch.fg !== undefined)
|
|
123
|
+
cell.fg = compositeOver(patch.fg, cell.bg);
|
|
115
124
|
if (patch.style !== undefined)
|
|
116
125
|
cell.style = patch.style;
|
|
117
126
|
if (patch.width !== undefined)
|
|
@@ -132,10 +141,11 @@ export class Grid {
|
|
|
132
141
|
}
|
|
133
142
|
cont.char = "";
|
|
134
143
|
cont.width = 0;
|
|
144
|
+
// Продолжение — та же ячейка: берёт уже скомпозиченные цвета головы.
|
|
135
145
|
if (patch.fg !== undefined)
|
|
136
|
-
cont.fg =
|
|
146
|
+
cont.fg = cell.fg;
|
|
137
147
|
if (patch.bg !== undefined)
|
|
138
|
-
cont.bg =
|
|
148
|
+
cont.bg = cell.bg;
|
|
139
149
|
if (patch.style !== undefined)
|
|
140
150
|
cont.style = patch.style;
|
|
141
151
|
}
|
package/package.json
CHANGED